@mytai20100/opencode-browser 0.0.1
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/LICENSE +21 -0
- package/README.md +161 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1285 -0
- package/dist/index.js.map +1 -0
- package/package.json +58 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1285 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
3
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
5
|
+
import { WebSocketServer, WebSocket } from "ws";
|
|
6
|
+
const wss = new WebSocketServer({
|
|
7
|
+
port: 3002,
|
|
8
|
+
host: "0.0.0.0",
|
|
9
|
+
verifyClient: () => true,
|
|
10
|
+
});
|
|
11
|
+
console.error("WebSocket server started on port 3002");
|
|
12
|
+
// Keep connections alive with server-side ping every 25s
|
|
13
|
+
const SERVER_PING_INTERVAL = 25000;
|
|
14
|
+
wss.on("connection", (ws) => {
|
|
15
|
+
console.error("Extension connected");
|
|
16
|
+
ws.isAlive = true;
|
|
17
|
+
ws.on("pong", () => { ws.isAlive = true; });
|
|
18
|
+
ws.on("message", (data) => {
|
|
19
|
+
try {
|
|
20
|
+
const msg = JSON.parse(data.toString());
|
|
21
|
+
// Ignore keep-alive pings from extension
|
|
22
|
+
if (msg.type === "ping")
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
catch (e) { }
|
|
26
|
+
});
|
|
27
|
+
ws.on("close", () => { console.error("Extension disconnected"); });
|
|
28
|
+
});
|
|
29
|
+
// Server-side heartbeat: ping all clients, drop dead ones
|
|
30
|
+
const heartbeat = setInterval(() => {
|
|
31
|
+
wss.clients.forEach((ws) => {
|
|
32
|
+
if (ws.isAlive === false) {
|
|
33
|
+
console.error("Dropping dead connection");
|
|
34
|
+
return ws.terminate();
|
|
35
|
+
}
|
|
36
|
+
ws.isAlive = false;
|
|
37
|
+
ws.ping();
|
|
38
|
+
});
|
|
39
|
+
}, SERVER_PING_INTERVAL);
|
|
40
|
+
const server = new Server({
|
|
41
|
+
name: "opencode-brower",
|
|
42
|
+
version: "opencode beta-2a",
|
|
43
|
+
}, {
|
|
44
|
+
capabilities: {
|
|
45
|
+
tools: {},
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
async function callExtension(method, params) {
|
|
49
|
+
return new Promise((resolve, reject) => {
|
|
50
|
+
const id = Math.random().toString(36).substring(7);
|
|
51
|
+
const message = JSON.stringify({ id, method, params });
|
|
52
|
+
let sent = false;
|
|
53
|
+
wss.clients.forEach((client) => {
|
|
54
|
+
if (client.readyState === WebSocket.OPEN) {
|
|
55
|
+
sent = true;
|
|
56
|
+
const listener = (data) => {
|
|
57
|
+
try {
|
|
58
|
+
const response = JSON.parse(data.toString());
|
|
59
|
+
// Ignore keep-alive pings
|
|
60
|
+
if (response.type === "ping")
|
|
61
|
+
return;
|
|
62
|
+
if (response.id === id) {
|
|
63
|
+
client.removeListener("message", listener);
|
|
64
|
+
if (response.error)
|
|
65
|
+
reject(new Error(response.error));
|
|
66
|
+
else
|
|
67
|
+
resolve(response.result);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
catch (e) { }
|
|
71
|
+
};
|
|
72
|
+
client.on("message", listener);
|
|
73
|
+
client.send(message);
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
if (!sent) {
|
|
77
|
+
reject(new Error("No extension connected"));
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
setTimeout(() => {
|
|
81
|
+
reject(new Error("Timeout waiting for extension response"));
|
|
82
|
+
}, 30000);
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
const TOOLS = [
|
|
86
|
+
// TABS VIEWING & QUERYING
|
|
87
|
+
{
|
|
88
|
+
name: "chrome_list_tabs",
|
|
89
|
+
description: "List all open tabs in Chrome with their id, title, url, active status, window, pinned, muted, audible states",
|
|
90
|
+
inputSchema: { type: "object", properties: {} },
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
name: "chrome_get_active_tab",
|
|
94
|
+
description: "Get info about the currently active tab",
|
|
95
|
+
inputSchema: { type: "object", properties: {} },
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
name: "chrome_get_tab_info",
|
|
99
|
+
description: "Get detailed info about a specific tab by its id",
|
|
100
|
+
inputSchema: {
|
|
101
|
+
type: "object",
|
|
102
|
+
properties: { tabId: { type: "number", description: "Tab ID" } },
|
|
103
|
+
required: ["tabId"],
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
name: "chrome_search_tabs",
|
|
108
|
+
description: "Search open tabs by title or URL keyword",
|
|
109
|
+
inputSchema: {
|
|
110
|
+
type: "object",
|
|
111
|
+
properties: { query: { type: "string", description: "Search term" } },
|
|
112
|
+
required: ["query"],
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
// TAB MANAGEMENT
|
|
116
|
+
{
|
|
117
|
+
name: "chrome_navigate",
|
|
118
|
+
description: "Navigate a tab to a URL. Optionally specify tabId; defaults to active tab",
|
|
119
|
+
inputSchema: {
|
|
120
|
+
type: "object",
|
|
121
|
+
properties: {
|
|
122
|
+
url: { type: "string" },
|
|
123
|
+
tabId: { type: "number", description: "Target tab ID (optional)" },
|
|
124
|
+
},
|
|
125
|
+
required: ["url"],
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
name: "chrome_new_tab",
|
|
130
|
+
description: "Open a new tab, optionally with a URL",
|
|
131
|
+
inputSchema: {
|
|
132
|
+
type: "object",
|
|
133
|
+
properties: {
|
|
134
|
+
url: { type: "string" },
|
|
135
|
+
active: { type: "boolean", description: "Make it active (default true)" },
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
name: "chrome_close_tab",
|
|
141
|
+
description: "Close a tab by id (defaults to active tab)",
|
|
142
|
+
inputSchema: {
|
|
143
|
+
type: "object",
|
|
144
|
+
properties: { tabId: { type: "number" } },
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
name: "chrome_close_tabs",
|
|
149
|
+
description: "Close multiple tabs by their ids",
|
|
150
|
+
inputSchema: {
|
|
151
|
+
type: "object",
|
|
152
|
+
properties: {
|
|
153
|
+
tabIds: { type: "array", items: { type: "number" }, description: "Array of tab IDs" },
|
|
154
|
+
},
|
|
155
|
+
required: ["tabIds"],
|
|
156
|
+
},
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
name: "chrome_switch_tab",
|
|
160
|
+
description: "Switch to (focus) a specific tab by id",
|
|
161
|
+
inputSchema: {
|
|
162
|
+
type: "object",
|
|
163
|
+
properties: { tabId: { type: "number" } },
|
|
164
|
+
required: ["tabId"],
|
|
165
|
+
},
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
name: "chrome_duplicate_tab",
|
|
169
|
+
description: "Duplicate a tab (defaults to active tab)",
|
|
170
|
+
inputSchema: {
|
|
171
|
+
type: "object",
|
|
172
|
+
properties: { tabId: { type: "number" } },
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
name: "chrome_pin_tab",
|
|
177
|
+
description: "Pin or unpin a tab",
|
|
178
|
+
inputSchema: {
|
|
179
|
+
type: "object",
|
|
180
|
+
properties: {
|
|
181
|
+
tabId: { type: "number" },
|
|
182
|
+
pinned: { type: "boolean", description: "true to pin, false to unpin (default true)" },
|
|
183
|
+
},
|
|
184
|
+
},
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
name: "chrome_mute_tab",
|
|
188
|
+
description: "Mute or unmute a tab",
|
|
189
|
+
inputSchema: {
|
|
190
|
+
type: "object",
|
|
191
|
+
properties: {
|
|
192
|
+
tabId: { type: "number" },
|
|
193
|
+
muted: { type: "boolean", description: "true to mute, false to unmute (default true)" },
|
|
194
|
+
},
|
|
195
|
+
},
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
name: "chrome_reload_tab",
|
|
199
|
+
description: "Reload a tab",
|
|
200
|
+
inputSchema: {
|
|
201
|
+
type: "object",
|
|
202
|
+
properties: {
|
|
203
|
+
tabId: { type: "number" },
|
|
204
|
+
bypassCache: { type: "boolean" },
|
|
205
|
+
},
|
|
206
|
+
},
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
name: "chrome_move_tab",
|
|
210
|
+
description: "Move a tab to a different position or window",
|
|
211
|
+
inputSchema: {
|
|
212
|
+
type: "object",
|
|
213
|
+
properties: {
|
|
214
|
+
tabId: { type: "number" },
|
|
215
|
+
index: { type: "number" },
|
|
216
|
+
windowId: { type: "number" },
|
|
217
|
+
},
|
|
218
|
+
required: ["tabId", "index"],
|
|
219
|
+
},
|
|
220
|
+
},
|
|
221
|
+
// WINDOWS
|
|
222
|
+
{
|
|
223
|
+
name: "chrome_list_windows",
|
|
224
|
+
description: "List all open Chrome windows with their id, state, focused, tab count",
|
|
225
|
+
inputSchema: { type: "object", properties: {} },
|
|
226
|
+
},
|
|
227
|
+
{
|
|
228
|
+
name: "chrome_new_window",
|
|
229
|
+
description: "Open a new browser window",
|
|
230
|
+
inputSchema: {
|
|
231
|
+
type: "object",
|
|
232
|
+
properties: {
|
|
233
|
+
url: { type: "string" },
|
|
234
|
+
incognito: { type: "boolean" },
|
|
235
|
+
state: { type: "string", enum: ["normal", "minimized", "maximized", "fullscreen"] },
|
|
236
|
+
},
|
|
237
|
+
},
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
name: "chrome_close_window",
|
|
241
|
+
description: "Close a browser window",
|
|
242
|
+
inputSchema: {
|
|
243
|
+
type: "object",
|
|
244
|
+
properties: { windowId: { type: "number" } },
|
|
245
|
+
required: ["windowId"],
|
|
246
|
+
},
|
|
247
|
+
},
|
|
248
|
+
// SCREENSHOT
|
|
249
|
+
{
|
|
250
|
+
name: "chrome_screenshot",
|
|
251
|
+
description: "Take a screenshot of the visible area of the current tab. Returns a base64 data URL.",
|
|
252
|
+
inputSchema: {
|
|
253
|
+
type: "object",
|
|
254
|
+
properties: {
|
|
255
|
+
format: { type: "string", enum: ["png", "jpeg"] },
|
|
256
|
+
quality: { type: "number", description: "JPEG quality 0-100" },
|
|
257
|
+
windowId: { type: "number" },
|
|
258
|
+
},
|
|
259
|
+
},
|
|
260
|
+
},
|
|
261
|
+
// PAGE INTERACTION
|
|
262
|
+
{
|
|
263
|
+
name: "chrome_click",
|
|
264
|
+
description: "Click an element by CSS selector in the current (or specified) tab",
|
|
265
|
+
inputSchema: {
|
|
266
|
+
type: "object",
|
|
267
|
+
properties: {
|
|
268
|
+
selector: { type: "string" },
|
|
269
|
+
tabId: { type: "number" },
|
|
270
|
+
},
|
|
271
|
+
required: ["selector"],
|
|
272
|
+
},
|
|
273
|
+
},
|
|
274
|
+
{
|
|
275
|
+
name: "chrome_type",
|
|
276
|
+
description: "Type text into an input element by CSS selector",
|
|
277
|
+
inputSchema: {
|
|
278
|
+
type: "object",
|
|
279
|
+
properties: {
|
|
280
|
+
selector: { type: "string" },
|
|
281
|
+
text: { type: "string" },
|
|
282
|
+
tabId: { type: "number" },
|
|
283
|
+
simulate: { type: "boolean", description: "Simulate key-by-key typing (default false)" },
|
|
284
|
+
},
|
|
285
|
+
required: ["selector", "text"],
|
|
286
|
+
},
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
name: "chrome_hover",
|
|
290
|
+
description: "Hover over an element by CSS selector",
|
|
291
|
+
inputSchema: {
|
|
292
|
+
type: "object",
|
|
293
|
+
properties: { selector: { type: "string" }, tabId: { type: "number" } },
|
|
294
|
+
required: ["selector"],
|
|
295
|
+
},
|
|
296
|
+
},
|
|
297
|
+
{
|
|
298
|
+
name: "chrome_select",
|
|
299
|
+
description: "Select an option in a <select> element",
|
|
300
|
+
inputSchema: {
|
|
301
|
+
type: "object",
|
|
302
|
+
properties: {
|
|
303
|
+
selector: { type: "string" },
|
|
304
|
+
value: { type: "string" },
|
|
305
|
+
tabId: { type: "number" },
|
|
306
|
+
},
|
|
307
|
+
required: ["selector", "value"],
|
|
308
|
+
},
|
|
309
|
+
},
|
|
310
|
+
{
|
|
311
|
+
name: "chrome_scroll",
|
|
312
|
+
description: "Scroll the page or an element by x/y pixels",
|
|
313
|
+
inputSchema: {
|
|
314
|
+
type: "object",
|
|
315
|
+
properties: {
|
|
316
|
+
x: { type: "number" },
|
|
317
|
+
y: { type: "number" },
|
|
318
|
+
selector: { type: "string", description: "Scroll a specific element (optional)" },
|
|
319
|
+
tabId: { type: "number" },
|
|
320
|
+
},
|
|
321
|
+
},
|
|
322
|
+
},
|
|
323
|
+
{
|
|
324
|
+
name: "chrome_scroll_to",
|
|
325
|
+
description: "Scroll an element into view by CSS selector",
|
|
326
|
+
inputSchema: {
|
|
327
|
+
type: "object",
|
|
328
|
+
properties: { selector: { type: "string" }, tabId: { type: "number" } },
|
|
329
|
+
required: ["selector"],
|
|
330
|
+
},
|
|
331
|
+
},
|
|
332
|
+
{
|
|
333
|
+
name: "chrome_key_press",
|
|
334
|
+
description: "Dispatch a keyboard event (e.g. 'Enter', 'Escape', 'Tab') on the page or element",
|
|
335
|
+
inputSchema: {
|
|
336
|
+
type: "object",
|
|
337
|
+
properties: {
|
|
338
|
+
key: { type: "string", description: "Key name e.g. 'Enter', 'Escape', 'ArrowDown'" },
|
|
339
|
+
selector: { type: "string", description: "Target element (optional, defaults to focused element)" },
|
|
340
|
+
tabId: { type: "number" },
|
|
341
|
+
},
|
|
342
|
+
required: ["key"],
|
|
343
|
+
},
|
|
344
|
+
},
|
|
345
|
+
{
|
|
346
|
+
name: "chrome_wait_for_element",
|
|
347
|
+
description: "Wait until a CSS selector appears in the DOM",
|
|
348
|
+
inputSchema: {
|
|
349
|
+
type: "object",
|
|
350
|
+
properties: {
|
|
351
|
+
selector: { type: "string" },
|
|
352
|
+
timeout: { type: "number", description: "Max wait in ms (default 5000)" },
|
|
353
|
+
tabId: { type: "number" },
|
|
354
|
+
},
|
|
355
|
+
required: ["selector"],
|
|
356
|
+
},
|
|
357
|
+
},
|
|
358
|
+
// PAGE CONTENT
|
|
359
|
+
{
|
|
360
|
+
name: "chrome_get_content",
|
|
361
|
+
description: "Get the visible text content of the current (or specified) tab's page",
|
|
362
|
+
inputSchema: {
|
|
363
|
+
type: "object",
|
|
364
|
+
properties: { tabId: { type: "number" } },
|
|
365
|
+
},
|
|
366
|
+
},
|
|
367
|
+
{
|
|
368
|
+
name: "chrome_get_html",
|
|
369
|
+
description: "Get the outer HTML of an element (or full page if no selector given)",
|
|
370
|
+
inputSchema: {
|
|
371
|
+
type: "object",
|
|
372
|
+
properties: {
|
|
373
|
+
selector: { type: "string" },
|
|
374
|
+
tabId: { type: "number" },
|
|
375
|
+
},
|
|
376
|
+
},
|
|
377
|
+
},
|
|
378
|
+
{
|
|
379
|
+
name: "chrome_get_element_info",
|
|
380
|
+
description: "Get detailed info about a DOM element: tag, id, class, text, attributes, bounding box, visibility",
|
|
381
|
+
inputSchema: {
|
|
382
|
+
type: "object",
|
|
383
|
+
properties: { selector: { type: "string" }, tabId: { type: "number" } },
|
|
384
|
+
required: ["selector"],
|
|
385
|
+
},
|
|
386
|
+
},
|
|
387
|
+
{
|
|
388
|
+
name: "chrome_find_elements",
|
|
389
|
+
description: "Find all elements matching a CSS selector and return their properties",
|
|
390
|
+
inputSchema: {
|
|
391
|
+
type: "object",
|
|
392
|
+
properties: {
|
|
393
|
+
selector: { type: "string" },
|
|
394
|
+
limit: { type: "number", description: "Max results (default 50)" },
|
|
395
|
+
tabId: { type: "number" },
|
|
396
|
+
},
|
|
397
|
+
required: ["selector"],
|
|
398
|
+
},
|
|
399
|
+
},
|
|
400
|
+
{
|
|
401
|
+
name: "chrome_get_page_info",
|
|
402
|
+
description: "Get page metadata: title, URL, scroll position, viewport size, links, meta description",
|
|
403
|
+
inputSchema: {
|
|
404
|
+
type: "object",
|
|
405
|
+
properties: { tabId: { type: "number" } },
|
|
406
|
+
},
|
|
407
|
+
},
|
|
408
|
+
{
|
|
409
|
+
name: "chrome_execute_script",
|
|
410
|
+
description: "Execute arbitrary JavaScript in the current (or specified) tab with full DOM access",
|
|
411
|
+
inputSchema: {
|
|
412
|
+
type: "object",
|
|
413
|
+
properties: {
|
|
414
|
+
code: { type: "string" },
|
|
415
|
+
tabId: { type: "number" },
|
|
416
|
+
allFrames: { type: "boolean" },
|
|
417
|
+
},
|
|
418
|
+
required: ["code"],
|
|
419
|
+
},
|
|
420
|
+
},
|
|
421
|
+
// NAVIGATION HISTORY
|
|
422
|
+
{
|
|
423
|
+
name: "chrome_go_back",
|
|
424
|
+
description: "Navigate back in the current tab's history",
|
|
425
|
+
inputSchema: {
|
|
426
|
+
type: "object",
|
|
427
|
+
properties: { tabId: { type: "number" } },
|
|
428
|
+
},
|
|
429
|
+
},
|
|
430
|
+
{
|
|
431
|
+
name: "chrome_go_forward",
|
|
432
|
+
description: "Navigate forward in the current tab's history",
|
|
433
|
+
inputSchema: {
|
|
434
|
+
type: "object",
|
|
435
|
+
properties: { tabId: { type: "number" } },
|
|
436
|
+
},
|
|
437
|
+
},
|
|
438
|
+
{
|
|
439
|
+
name: "chrome_go_home",
|
|
440
|
+
description: "Navigate the active tab to the new tab page",
|
|
441
|
+
inputSchema: { type: "object", properties: {} },
|
|
442
|
+
},
|
|
443
|
+
// COOKIES
|
|
444
|
+
{
|
|
445
|
+
name: "chrome_get_cookies",
|
|
446
|
+
description: "Get all cookies for a given URL",
|
|
447
|
+
inputSchema: {
|
|
448
|
+
type: "object",
|
|
449
|
+
properties: { url: { type: "string" } },
|
|
450
|
+
required: ["url"],
|
|
451
|
+
},
|
|
452
|
+
},
|
|
453
|
+
{
|
|
454
|
+
name: "chrome_set_cookie",
|
|
455
|
+
description: "Set a cookie for a URL",
|
|
456
|
+
inputSchema: {
|
|
457
|
+
type: "object",
|
|
458
|
+
properties: {
|
|
459
|
+
url: { type: "string" },
|
|
460
|
+
name: { type: "string" },
|
|
461
|
+
value: { type: "string" },
|
|
462
|
+
domain: { type: "string" },
|
|
463
|
+
path: { type: "string" },
|
|
464
|
+
secure: { type: "boolean" },
|
|
465
|
+
httpOnly: { type: "boolean" },
|
|
466
|
+
},
|
|
467
|
+
required: ["url", "name", "value"],
|
|
468
|
+
},
|
|
469
|
+
},
|
|
470
|
+
{
|
|
471
|
+
name: "chrome_delete_cookie",
|
|
472
|
+
description: "Delete a specific cookie",
|
|
473
|
+
inputSchema: {
|
|
474
|
+
type: "object",
|
|
475
|
+
properties: { url: { type: "string" }, name: { type: "string" } },
|
|
476
|
+
required: ["url", "name"],
|
|
477
|
+
},
|
|
478
|
+
},
|
|
479
|
+
// LOCAL STORAGE
|
|
480
|
+
{
|
|
481
|
+
name: "chrome_get_local_storage",
|
|
482
|
+
description: "Get localStorage value(s) from the current page",
|
|
483
|
+
inputSchema: {
|
|
484
|
+
type: "object",
|
|
485
|
+
properties: {
|
|
486
|
+
key: { type: "string", description: "Specific key (omit to get all)" },
|
|
487
|
+
tabId: { type: "number" },
|
|
488
|
+
},
|
|
489
|
+
},
|
|
490
|
+
},
|
|
491
|
+
{
|
|
492
|
+
name: "chrome_set_local_storage",
|
|
493
|
+
description: "Set a localStorage value on the current page",
|
|
494
|
+
inputSchema: {
|
|
495
|
+
type: "object",
|
|
496
|
+
properties: {
|
|
497
|
+
key: { type: "string" },
|
|
498
|
+
value: { type: "string" },
|
|
499
|
+
tabId: { type: "number" },
|
|
500
|
+
},
|
|
501
|
+
required: ["key", "value"],
|
|
502
|
+
},
|
|
503
|
+
},
|
|
504
|
+
{
|
|
505
|
+
name: "chrome_clear_local_storage",
|
|
506
|
+
description: "Clear all localStorage on the current page",
|
|
507
|
+
inputSchema: {
|
|
508
|
+
type: "object",
|
|
509
|
+
properties: { tabId: { type: "number" } },
|
|
510
|
+
},
|
|
511
|
+
},
|
|
512
|
+
// HISTORY & BOOKMARKS
|
|
513
|
+
{
|
|
514
|
+
name: "chrome_get_history",
|
|
515
|
+
description: "Search browser history by text query",
|
|
516
|
+
inputSchema: {
|
|
517
|
+
type: "object",
|
|
518
|
+
properties: {
|
|
519
|
+
query: { type: "string" },
|
|
520
|
+
maxResults: { type: "number" },
|
|
521
|
+
startTime: { type: "number", description: "Start time in ms epoch" },
|
|
522
|
+
},
|
|
523
|
+
},
|
|
524
|
+
},
|
|
525
|
+
{
|
|
526
|
+
name: "chrome_add_bookmark",
|
|
527
|
+
description: "Add a bookmark",
|
|
528
|
+
inputSchema: {
|
|
529
|
+
type: "object",
|
|
530
|
+
properties: {
|
|
531
|
+
title: { type: "string" },
|
|
532
|
+
url: { type: "string" },
|
|
533
|
+
parentId: { type: "string" },
|
|
534
|
+
},
|
|
535
|
+
required: ["title", "url"],
|
|
536
|
+
},
|
|
537
|
+
},
|
|
538
|
+
{
|
|
539
|
+
name: "chrome_search_bookmarks",
|
|
540
|
+
description: "Search bookmarks by title or URL",
|
|
541
|
+
inputSchema: {
|
|
542
|
+
type: "object",
|
|
543
|
+
properties: {
|
|
544
|
+
query: { type: "string" },
|
|
545
|
+
limit: { type: "number" },
|
|
546
|
+
},
|
|
547
|
+
required: ["query"],
|
|
548
|
+
},
|
|
549
|
+
},
|
|
550
|
+
{
|
|
551
|
+
name: "chrome_get_bookmarks",
|
|
552
|
+
description: "Get all bookmarks in a flat list",
|
|
553
|
+
inputSchema: { type: "object", properties: {} },
|
|
554
|
+
},
|
|
555
|
+
// DOWNLOADS
|
|
556
|
+
{
|
|
557
|
+
name: "chrome_download",
|
|
558
|
+
description: "Download a file from a URL",
|
|
559
|
+
inputSchema: {
|
|
560
|
+
type: "object",
|
|
561
|
+
properties: {
|
|
562
|
+
url: { type: "string" },
|
|
563
|
+
filename: { type: "string" },
|
|
564
|
+
saveAs: { type: "boolean" },
|
|
565
|
+
},
|
|
566
|
+
required: ["url"],
|
|
567
|
+
},
|
|
568
|
+
},
|
|
569
|
+
{
|
|
570
|
+
name: "chrome_list_downloads",
|
|
571
|
+
description: "List recent downloads",
|
|
572
|
+
inputSchema: {
|
|
573
|
+
type: "object",
|
|
574
|
+
properties: {
|
|
575
|
+
limit: { type: "number" },
|
|
576
|
+
state: { type: "string", enum: ["in_progress", "interrupted", "complete"] },
|
|
577
|
+
},
|
|
578
|
+
},
|
|
579
|
+
},
|
|
580
|
+
// TAB GROUPS
|
|
581
|
+
{
|
|
582
|
+
name: "chrome_group_tabs",
|
|
583
|
+
description: "Group tabs together, optionally with a title and color",
|
|
584
|
+
inputSchema: {
|
|
585
|
+
type: "object",
|
|
586
|
+
properties: {
|
|
587
|
+
tabIds: { type: "array", items: { type: "number" } },
|
|
588
|
+
title: { type: "string" },
|
|
589
|
+
color: {
|
|
590
|
+
type: "string",
|
|
591
|
+
enum: ["grey", "blue", "red", "yellow", "green", "pink", "purple", "cyan", "orange"],
|
|
592
|
+
},
|
|
593
|
+
},
|
|
594
|
+
required: ["tabIds"],
|
|
595
|
+
},
|
|
596
|
+
},
|
|
597
|
+
{
|
|
598
|
+
name: "chrome_ungroup_tabs",
|
|
599
|
+
description: "Ungroup tabs",
|
|
600
|
+
inputSchema: {
|
|
601
|
+
type: "object",
|
|
602
|
+
properties: { tabIds: { type: "array", items: { type: "number" } } },
|
|
603
|
+
required: ["tabIds"],
|
|
604
|
+
},
|
|
605
|
+
},
|
|
606
|
+
// NOTIFICATIONS
|
|
607
|
+
{
|
|
608
|
+
name: "chrome_notify",
|
|
609
|
+
description: "Show a desktop notification",
|
|
610
|
+
inputSchema: {
|
|
611
|
+
type: "object",
|
|
612
|
+
properties: {
|
|
613
|
+
title: { type: "string" },
|
|
614
|
+
message: { type: "string" },
|
|
615
|
+
},
|
|
616
|
+
required: ["message"],
|
|
617
|
+
},
|
|
618
|
+
},
|
|
619
|
+
// ZOOM
|
|
620
|
+
{
|
|
621
|
+
name: "chrome_set_zoom",
|
|
622
|
+
description: "Set zoom level of a tab (1.0 = 100%)",
|
|
623
|
+
inputSchema: {
|
|
624
|
+
type: "object",
|
|
625
|
+
properties: {
|
|
626
|
+
zoom: { type: "number" },
|
|
627
|
+
tabId: { type: "number" },
|
|
628
|
+
},
|
|
629
|
+
required: ["zoom"],
|
|
630
|
+
},
|
|
631
|
+
},
|
|
632
|
+
{
|
|
633
|
+
name: "chrome_get_zoom",
|
|
634
|
+
description: "Get current zoom level of a tab",
|
|
635
|
+
inputSchema: {
|
|
636
|
+
type: "object",
|
|
637
|
+
properties: { tabId: { type: "number" } },
|
|
638
|
+
},
|
|
639
|
+
},
|
|
640
|
+
// CLIPBOARD
|
|
641
|
+
{
|
|
642
|
+
name: "chrome_write_clipboard",
|
|
643
|
+
description: "Write text to the clipboard",
|
|
644
|
+
inputSchema: {
|
|
645
|
+
type: "object",
|
|
646
|
+
properties: { text: { type: "string" }, tabId: { type: "number" } },
|
|
647
|
+
required: ["text"],
|
|
648
|
+
},
|
|
649
|
+
},
|
|
650
|
+
{
|
|
651
|
+
name: "chrome_read_clipboard",
|
|
652
|
+
description: "Read text from the clipboard",
|
|
653
|
+
inputSchema: {
|
|
654
|
+
type: "object",
|
|
655
|
+
properties: { tabId: { type: "number" } },
|
|
656
|
+
},
|
|
657
|
+
},
|
|
658
|
+
// EXTENSION META
|
|
659
|
+
{
|
|
660
|
+
name: "chrome_get_extension_info",
|
|
661
|
+
description: "Get info about the Opencode Brower extension itself",
|
|
662
|
+
inputSchema: { type: "object", properties: {} },
|
|
663
|
+
},
|
|
664
|
+
// DEVTOOLS / DEBUGGER (CDP)
|
|
665
|
+
{
|
|
666
|
+
name: "chrome_debug_attach",
|
|
667
|
+
description: "Attach Chrome DevTools Protocol debugger to a tab. Enables Console, Network, Runtime, Performance domains for capturing logs and requests.",
|
|
668
|
+
inputSchema: {
|
|
669
|
+
type: "object",
|
|
670
|
+
properties: { tabId: { type: "number" } },
|
|
671
|
+
},
|
|
672
|
+
},
|
|
673
|
+
{
|
|
674
|
+
name: "chrome_debug_detach",
|
|
675
|
+
description: "Detach the debugger from a tab",
|
|
676
|
+
inputSchema: {
|
|
677
|
+
type: "object",
|
|
678
|
+
properties: { tabId: { type: "number" } },
|
|
679
|
+
},
|
|
680
|
+
},
|
|
681
|
+
{
|
|
682
|
+
name: "chrome_debug_get_logs",
|
|
683
|
+
description: "Get captured console logs from a debugged tab (console.log, warn, error, info)",
|
|
684
|
+
inputSchema: {
|
|
685
|
+
type: "object",
|
|
686
|
+
properties: {
|
|
687
|
+
tabId: { type: "number" },
|
|
688
|
+
limit: { type: "number", description: "Max number of logs to return (default 100)" },
|
|
689
|
+
level: { type: "string", description: "Filter by level: log, warn, error, info" },
|
|
690
|
+
},
|
|
691
|
+
},
|
|
692
|
+
},
|
|
693
|
+
{
|
|
694
|
+
name: "chrome_debug_clear_logs",
|
|
695
|
+
description: "Clear captured console logs for a tab",
|
|
696
|
+
inputSchema: {
|
|
697
|
+
type: "object",
|
|
698
|
+
properties: { tabId: { type: "number" } },
|
|
699
|
+
},
|
|
700
|
+
},
|
|
701
|
+
{
|
|
702
|
+
name: "chrome_debug_get_network",
|
|
703
|
+
description: "Get captured network requests (XHR, Fetch, etc.) from a debugged tab",
|
|
704
|
+
inputSchema: {
|
|
705
|
+
type: "object",
|
|
706
|
+
properties: {
|
|
707
|
+
tabId: { type: "number" },
|
|
708
|
+
limit: { type: "number", description: "Max results (default 100)" },
|
|
709
|
+
filter: { type: "string", description: "Filter URLs containing this string" },
|
|
710
|
+
},
|
|
711
|
+
},
|
|
712
|
+
},
|
|
713
|
+
{
|
|
714
|
+
name: "chrome_debug_clear_network",
|
|
715
|
+
description: "Clear captured network log for a tab",
|
|
716
|
+
inputSchema: {
|
|
717
|
+
type: "object",
|
|
718
|
+
properties: { tabId: { type: "number" } },
|
|
719
|
+
},
|
|
720
|
+
},
|
|
721
|
+
{
|
|
722
|
+
name: "chrome_debug_get_response_body",
|
|
723
|
+
description: "Get the response body of a captured network request by requestId",
|
|
724
|
+
inputSchema: {
|
|
725
|
+
type: "object",
|
|
726
|
+
properties: {
|
|
727
|
+
requestId: { type: "string" },
|
|
728
|
+
tabId: { type: "number" },
|
|
729
|
+
},
|
|
730
|
+
required: ["requestId"],
|
|
731
|
+
},
|
|
732
|
+
},
|
|
733
|
+
{
|
|
734
|
+
name: "chrome_debug_eval",
|
|
735
|
+
description: "Evaluate JavaScript in the page context via CDP Runtime.evaluate (bypasses sandbox, supports await)",
|
|
736
|
+
inputSchema: {
|
|
737
|
+
type: "object",
|
|
738
|
+
properties: {
|
|
739
|
+
code: { type: "string" },
|
|
740
|
+
tabId: { type: "number" },
|
|
741
|
+
},
|
|
742
|
+
required: ["code"],
|
|
743
|
+
},
|
|
744
|
+
},
|
|
745
|
+
{
|
|
746
|
+
name: "chrome_debug_get_performance",
|
|
747
|
+
description: "Get performance metrics from a debugged tab (JS heap, DOM nodes, layout count, etc.)",
|
|
748
|
+
inputSchema: {
|
|
749
|
+
type: "object",
|
|
750
|
+
properties: { tabId: { type: "number" } },
|
|
751
|
+
},
|
|
752
|
+
},
|
|
753
|
+
{
|
|
754
|
+
name: "chrome_debug_get_dom_snapshot",
|
|
755
|
+
description: "Capture a full DOM snapshot including layout, paint order, and bounding rects",
|
|
756
|
+
inputSchema: {
|
|
757
|
+
type: "object",
|
|
758
|
+
properties: { tabId: { type: "number" } },
|
|
759
|
+
},
|
|
760
|
+
},
|
|
761
|
+
{
|
|
762
|
+
name: "chrome_debug_set_breakpoint",
|
|
763
|
+
description: "Set a JavaScript breakpoint by URL and line number via CDP Debugger",
|
|
764
|
+
inputSchema: {
|
|
765
|
+
type: "object",
|
|
766
|
+
properties: {
|
|
767
|
+
url: { type: "string" },
|
|
768
|
+
line: { type: "number" },
|
|
769
|
+
column: { type: "number" },
|
|
770
|
+
tabId: { type: "number" },
|
|
771
|
+
},
|
|
772
|
+
required: ["url", "line"],
|
|
773
|
+
},
|
|
774
|
+
},
|
|
775
|
+
{
|
|
776
|
+
name: "chrome_debug_remove_breakpoint",
|
|
777
|
+
description: "Remove a JavaScript breakpoint by breakpointId",
|
|
778
|
+
inputSchema: {
|
|
779
|
+
type: "object",
|
|
780
|
+
properties: {
|
|
781
|
+
breakpointId: { type: "string" },
|
|
782
|
+
tabId: { type: "number" },
|
|
783
|
+
},
|
|
784
|
+
required: ["breakpointId"],
|
|
785
|
+
},
|
|
786
|
+
},
|
|
787
|
+
{
|
|
788
|
+
name: "chrome_debug_get_cookies",
|
|
789
|
+
description: "Get ALL cookies including HttpOnly ones via CDP Network.getAllCookies",
|
|
790
|
+
inputSchema: {
|
|
791
|
+
type: "object",
|
|
792
|
+
properties: { tabId: { type: "number" } },
|
|
793
|
+
},
|
|
794
|
+
},
|
|
795
|
+
{
|
|
796
|
+
name: "chrome_debug_set_xhr_breakpoint",
|
|
797
|
+
description: "Set an XHR/Fetch breakpoint to pause when a URL pattern is requested",
|
|
798
|
+
inputSchema: {
|
|
799
|
+
type: "object",
|
|
800
|
+
properties: {
|
|
801
|
+
url: { type: "string", description: "URL pattern to break on (empty = all XHR)" },
|
|
802
|
+
tabId: { type: "number" },
|
|
803
|
+
},
|
|
804
|
+
},
|
|
805
|
+
},
|
|
806
|
+
{
|
|
807
|
+
name: "chrome_debug_emulate_device",
|
|
808
|
+
description: "Emulate a mobile device (screen size, user agent, DPR)",
|
|
809
|
+
inputSchema: {
|
|
810
|
+
type: "object",
|
|
811
|
+
properties: {
|
|
812
|
+
width: { type: "number", description: "Screen width in px (default 375)" },
|
|
813
|
+
height: { type: "number", description: "Screen height in px (default 812)" },
|
|
814
|
+
deviceScaleFactor: { type: "number", description: "DPR (default 2)" },
|
|
815
|
+
mobile: { type: "boolean" },
|
|
816
|
+
userAgent: { type: "string" },
|
|
817
|
+
tabId: { type: "number" },
|
|
818
|
+
},
|
|
819
|
+
},
|
|
820
|
+
},
|
|
821
|
+
{
|
|
822
|
+
name: "chrome_debug_emulate_network",
|
|
823
|
+
description: "Throttle network speed to emulate different conditions",
|
|
824
|
+
inputSchema: {
|
|
825
|
+
type: "object",
|
|
826
|
+
properties: {
|
|
827
|
+
preset: { type: "string", enum: ["offline", "slow3g", "fast3g", "none"], description: "Preset throttle condition" },
|
|
828
|
+
offline: { type: "boolean" },
|
|
829
|
+
download: { type: "number", description: "Download in bytes/sec" },
|
|
830
|
+
upload: { type: "number", description: "Upload in bytes/sec" },
|
|
831
|
+
latency: { type: "number", description: "Latency in ms" },
|
|
832
|
+
tabId: { type: "number" },
|
|
833
|
+
},
|
|
834
|
+
},
|
|
835
|
+
},
|
|
836
|
+
{
|
|
837
|
+
name: "chrome_debug_block_urls",
|
|
838
|
+
description: "Block specific URL patterns from loading (ads, tracking, API calls)",
|
|
839
|
+
inputSchema: {
|
|
840
|
+
type: "object",
|
|
841
|
+
properties: {
|
|
842
|
+
urls: { type: "array", items: { type: "string" }, description: "URL patterns to block" },
|
|
843
|
+
tabId: { type: "number" },
|
|
844
|
+
},
|
|
845
|
+
required: ["urls"],
|
|
846
|
+
},
|
|
847
|
+
},
|
|
848
|
+
{
|
|
849
|
+
name: "chrome_debug_get_storage",
|
|
850
|
+
description: "Get DOM storage (localStorage/sessionStorage) for a specific origin via CDP",
|
|
851
|
+
inputSchema: {
|
|
852
|
+
type: "object",
|
|
853
|
+
properties: {
|
|
854
|
+
origin: { type: "string", description: "Security origin e.g. https://example.com" },
|
|
855
|
+
isLocal: { type: "boolean", description: "true = localStorage, false = sessionStorage (default true)" },
|
|
856
|
+
tabId: { type: "number" },
|
|
857
|
+
},
|
|
858
|
+
required: ["origin"],
|
|
859
|
+
},
|
|
860
|
+
},
|
|
861
|
+
{
|
|
862
|
+
name: "chrome_debug_send_command",
|
|
863
|
+
description: "Send a raw Chrome DevTools Protocol (CDP) command for advanced debugging",
|
|
864
|
+
inputSchema: {
|
|
865
|
+
type: "object",
|
|
866
|
+
properties: {
|
|
867
|
+
command: { type: "string", description: "CDP method e.g. 'Network.enable', 'DOM.getDocument'" },
|
|
868
|
+
commandParams: { type: "object", description: "Parameters for the CDP command" },
|
|
869
|
+
tabId: { type: "number" },
|
|
870
|
+
},
|
|
871
|
+
required: ["command"],
|
|
872
|
+
},
|
|
873
|
+
},
|
|
874
|
+
// ACCESSIBILITY
|
|
875
|
+
{
|
|
876
|
+
name: "chrome_get_accessibility_tree",
|
|
877
|
+
description: "Get the full accessibility tree of the page via CDP AX (useful for finding elements by label/role)",
|
|
878
|
+
inputSchema: {
|
|
879
|
+
type: "object",
|
|
880
|
+
properties: {
|
|
881
|
+
tabId: { type: "number" },
|
|
882
|
+
limit: { type: "number", description: "Max nodes to return (default 200)" },
|
|
883
|
+
},
|
|
884
|
+
},
|
|
885
|
+
},
|
|
886
|
+
{
|
|
887
|
+
name: "chrome_find_accessible_nodes",
|
|
888
|
+
description: "Find accessibility nodes by name/label and/or role (button, textbox, link, etc.)",
|
|
889
|
+
inputSchema: {
|
|
890
|
+
type: "object",
|
|
891
|
+
properties: {
|
|
892
|
+
query: { type: "string", description: "Text/label to search for" },
|
|
893
|
+
role: { type: "string", description: "ARIA role to filter by e.g. button, textbox, link" },
|
|
894
|
+
limit: { type: "number" },
|
|
895
|
+
tabId: { type: "number" },
|
|
896
|
+
},
|
|
897
|
+
},
|
|
898
|
+
},
|
|
899
|
+
// VISUAL / OC
|
|
900
|
+
{
|
|
901
|
+
name: "chrome_visual_click",
|
|
902
|
+
description: "Click at specific X/Y screen coordinates via CDP Input (useful when CSS selector is unavailable)",
|
|
903
|
+
inputSchema: {
|
|
904
|
+
type: "object",
|
|
905
|
+
properties: {
|
|
906
|
+
x: { type: "number", description: "X coordinate in pixels" },
|
|
907
|
+
y: { type: "number", description: "Y coordinate in pixels" },
|
|
908
|
+
tabId: { type: "number" },
|
|
909
|
+
},
|
|
910
|
+
required: ["x", "y"],
|
|
911
|
+
},
|
|
912
|
+
},
|
|
913
|
+
{
|
|
914
|
+
name: "chrome_ocr_page",
|
|
915
|
+
description: "Extract all visible text from the page with bounding box coordinates (like OCR)",
|
|
916
|
+
inputSchema: {
|
|
917
|
+
type: "object",
|
|
918
|
+
properties: {
|
|
919
|
+
tabId: { type: "number" },
|
|
920
|
+
limit: { type: "number", description: "Max text nodes (default 500)" },
|
|
921
|
+
},
|
|
922
|
+
},
|
|
923
|
+
},
|
|
924
|
+
{
|
|
925
|
+
name: "chrome_find_text_on_screen",
|
|
926
|
+
description: "Find text on the visible page and return its screen coordinates for visual clicking",
|
|
927
|
+
inputSchema: {
|
|
928
|
+
type: "object",
|
|
929
|
+
properties: {
|
|
930
|
+
query: { type: "string", description: "Text to find on screen" },
|
|
931
|
+
exact: { type: "boolean", description: "Exact match (default false)" },
|
|
932
|
+
tabId: { type: "number" },
|
|
933
|
+
},
|
|
934
|
+
required: ["query"],
|
|
935
|
+
},
|
|
936
|
+
},
|
|
937
|
+
// NETWORK INTERCEPT / MOCK
|
|
938
|
+
{
|
|
939
|
+
name: "chrome_intercept_request",
|
|
940
|
+
description: "Intercept all network requests matching a URL pattern via CDP Fetch. Captured requests available via debug_get_network.",
|
|
941
|
+
inputSchema: {
|
|
942
|
+
type: "object",
|
|
943
|
+
properties: {
|
|
944
|
+
urlPattern: { type: "string", description: "URL pattern to intercept e.g. '*/api/*'" },
|
|
945
|
+
tabId: { type: "number" },
|
|
946
|
+
},
|
|
947
|
+
},
|
|
948
|
+
},
|
|
949
|
+
{
|
|
950
|
+
name: "chrome_mock_response",
|
|
951
|
+
description: "Mock the response of a specific URL with custom status, headers, and body",
|
|
952
|
+
inputSchema: {
|
|
953
|
+
type: "object",
|
|
954
|
+
properties: {
|
|
955
|
+
url: { type: "string", description: "Exact URL to mock" },
|
|
956
|
+
status: { type: "number", description: "HTTP status code (default 200)" },
|
|
957
|
+
headers: { type: "object", description: "Response headers" },
|
|
958
|
+
body: { description: "Response body (string or object)" },
|
|
959
|
+
},
|
|
960
|
+
required: ["url"],
|
|
961
|
+
},
|
|
962
|
+
},
|
|
963
|
+
{
|
|
964
|
+
name: "chrome_modify_headers",
|
|
965
|
+
description: "Automatically modify request headers for all matching requests (add auth tokens, change User-Agent, etc.)",
|
|
966
|
+
inputSchema: {
|
|
967
|
+
type: "object",
|
|
968
|
+
properties: {
|
|
969
|
+
urlPattern: { type: "string", description: "URL pattern to match" },
|
|
970
|
+
headers: { type: "object", description: "Headers to add/override" },
|
|
971
|
+
tabId: { type: "number" },
|
|
972
|
+
},
|
|
973
|
+
required: ["headers"],
|
|
974
|
+
},
|
|
975
|
+
},
|
|
976
|
+
// SESSION
|
|
977
|
+
{
|
|
978
|
+
name: "chrome_save_session",
|
|
979
|
+
description: "Save the current session (cookies + localStorage) for later restoration",
|
|
980
|
+
inputSchema: {
|
|
981
|
+
type: "object",
|
|
982
|
+
properties: {
|
|
983
|
+
name: { type: "string", description: "Session name/key to save as" },
|
|
984
|
+
tabId: { type: "number" },
|
|
985
|
+
},
|
|
986
|
+
required: ["name"],
|
|
987
|
+
},
|
|
988
|
+
},
|
|
989
|
+
{
|
|
990
|
+
name: "chrome_restore_session",
|
|
991
|
+
description: "Restore a previously saved session (cookies + localStorage)",
|
|
992
|
+
inputSchema: {
|
|
993
|
+
type: "object",
|
|
994
|
+
properties: {
|
|
995
|
+
name: { type: "string", description: "Session name/key to restore" },
|
|
996
|
+
tabId: { type: "number" },
|
|
997
|
+
},
|
|
998
|
+
required: ["name"],
|
|
999
|
+
},
|
|
1000
|
+
},
|
|
1001
|
+
// EVENTS & DOM WATCH
|
|
1002
|
+
{
|
|
1003
|
+
name: "chrome_subscribe_events",
|
|
1004
|
+
description: "Subscribe to DOM events (click, input, submit, etc.) and log them for later retrieval via get_workflow_context",
|
|
1005
|
+
inputSchema: {
|
|
1006
|
+
type: "object",
|
|
1007
|
+
properties: {
|
|
1008
|
+
events: {
|
|
1009
|
+
type: "array", items: { type: "string" },
|
|
1010
|
+
description: "List of events to listen to (default: click, input, submit, change, keydown)"
|
|
1011
|
+
},
|
|
1012
|
+
tabId: { type: "number" },
|
|
1013
|
+
},
|
|
1014
|
+
},
|
|
1015
|
+
},
|
|
1016
|
+
{
|
|
1017
|
+
name: "chrome_watch_dom_changes",
|
|
1018
|
+
description: "Watch for DOM mutations (added/removed elements, attribute changes) via MutationObserver",
|
|
1019
|
+
inputSchema: {
|
|
1020
|
+
type: "object",
|
|
1021
|
+
properties: {
|
|
1022
|
+
selector: { type: "string", description: "Root element to observe (default: document.body)" },
|
|
1023
|
+
limit: { type: "number", description: "Max mutations to store (default 500)" },
|
|
1024
|
+
tabId: { type: "number" },
|
|
1025
|
+
},
|
|
1026
|
+
},
|
|
1027
|
+
},
|
|
1028
|
+
// IFRAMES
|
|
1029
|
+
{
|
|
1030
|
+
name: "chrome_list_iframes",
|
|
1031
|
+
description: "List all iframes on the current page with src, id, name, and dimensions",
|
|
1032
|
+
inputSchema: {
|
|
1033
|
+
type: "object",
|
|
1034
|
+
properties: { tabId: { type: "number" } },
|
|
1035
|
+
},
|
|
1036
|
+
},
|
|
1037
|
+
{
|
|
1038
|
+
name: "chrome_switch_iframe",
|
|
1039
|
+
description: "Execute JavaScript code inside a specific iframe by frame index",
|
|
1040
|
+
inputSchema: {
|
|
1041
|
+
type: "object",
|
|
1042
|
+
properties: {
|
|
1043
|
+
frameIndex: { type: "number", description: "Index of the iframe (0-based)" },
|
|
1044
|
+
code: { type: "string", description: "JavaScript to execute inside the iframe" },
|
|
1045
|
+
tabId: { type: "number" },
|
|
1046
|
+
},
|
|
1047
|
+
required: ["frameIndex"],
|
|
1048
|
+
},
|
|
1049
|
+
},
|
|
1050
|
+
// FILE UPLOAD
|
|
1051
|
+
{
|
|
1052
|
+
name: "chrome_upload_file",
|
|
1053
|
+
description: "Set files on a file input element via CDP DOM.setFileInputFiles",
|
|
1054
|
+
inputSchema: {
|
|
1055
|
+
type: "object",
|
|
1056
|
+
properties: {
|
|
1057
|
+
selector: { type: "string", description: "CSS selector of the file input element" },
|
|
1058
|
+
files: { type: "array", items: { type: "string" }, description: "Array of absolute file paths to upload" },
|
|
1059
|
+
tabId: { type: "number" },
|
|
1060
|
+
},
|
|
1061
|
+
required: ["selector", "files"],
|
|
1062
|
+
},
|
|
1063
|
+
},
|
|
1064
|
+
// PERMISSIONS
|
|
1065
|
+
{
|
|
1066
|
+
name: "chrome_grant_permissions",
|
|
1067
|
+
description: "Grant browser permissions to the current origin (geolocation, camera, microphone, notifications, etc.)",
|
|
1068
|
+
inputSchema: {
|
|
1069
|
+
type: "object",
|
|
1070
|
+
properties: {
|
|
1071
|
+
permissions: {
|
|
1072
|
+
type: "array", items: { type: "string" },
|
|
1073
|
+
description: "Permissions to grant e.g. ['geolocation', 'camera', 'microphone', 'notifications']"
|
|
1074
|
+
},
|
|
1075
|
+
tabId: { type: "number" },
|
|
1076
|
+
},
|
|
1077
|
+
required: ["permissions"],
|
|
1078
|
+
},
|
|
1079
|
+
},
|
|
1080
|
+
// VIRTUAL AUTHENTICATOR
|
|
1081
|
+
{
|
|
1082
|
+
name: "chrome_virtual_authenticator",
|
|
1083
|
+
description: "Add/remove a virtual WebAuthn authenticator for testing passkey/FIDO2 flows",
|
|
1084
|
+
inputSchema: {
|
|
1085
|
+
type: "object",
|
|
1086
|
+
properties: {
|
|
1087
|
+
action: { type: "string", enum: ["enable", "add", "remove"], description: "Action to perform" },
|
|
1088
|
+
protocol: { type: "string", enum: ["ctap2", "u2f"], description: "Authenticator protocol (default ctap2)" },
|
|
1089
|
+
transport: { type: "string", enum: ["usb", "nfc", "ble", "internal"], description: "Transport (default usb)" },
|
|
1090
|
+
authenticatorId: { type: "string", description: "ID for remove action" },
|
|
1091
|
+
hasResidentKey: { type: "boolean" },
|
|
1092
|
+
hasUserVerification: { type: "boolean" },
|
|
1093
|
+
isUserVerified: { type: "boolean" },
|
|
1094
|
+
tabId: { type: "number" },
|
|
1095
|
+
},
|
|
1096
|
+
required: ["action"],
|
|
1097
|
+
},
|
|
1098
|
+
},
|
|
1099
|
+
// WORKFLOW CONTEXT
|
|
1100
|
+
{
|
|
1101
|
+
name: "chrome_get_workflow_context",
|
|
1102
|
+
description: "Get a comprehensive snapshot of the current page state: forms, buttons, inputs, event log, mutation log — ideal for AI workflow planning",
|
|
1103
|
+
inputSchema: {
|
|
1104
|
+
type: "object",
|
|
1105
|
+
properties: { tabId: { type: "number" } },
|
|
1106
|
+
},
|
|
1107
|
+
},
|
|
1108
|
+
// HAR EXPORT
|
|
1109
|
+
{
|
|
1110
|
+
name: "chrome_export_har",
|
|
1111
|
+
description: "Export all captured network requests as a HAR (HTTP Archive) file",
|
|
1112
|
+
inputSchema: {
|
|
1113
|
+
type: "object",
|
|
1114
|
+
properties: { tabId: { type: "number" } },
|
|
1115
|
+
},
|
|
1116
|
+
},
|
|
1117
|
+
// REPLAY REQUEST
|
|
1118
|
+
{
|
|
1119
|
+
name: "chrome_replay_request",
|
|
1120
|
+
description: "Re-send any HTTP request with custom method, headers, and body directly from the page context",
|
|
1121
|
+
inputSchema: {
|
|
1122
|
+
type: "object",
|
|
1123
|
+
properties: {
|
|
1124
|
+
url: { type: "string" },
|
|
1125
|
+
method: { type: "string", description: "HTTP method (default GET)" },
|
|
1126
|
+
headers: { type: "object" },
|
|
1127
|
+
body: { type: "string" },
|
|
1128
|
+
tabId: { type: "number" },
|
|
1129
|
+
},
|
|
1130
|
+
required: ["url"],
|
|
1131
|
+
},
|
|
1132
|
+
},
|
|
1133
|
+
// TOOL GRAPH
|
|
1134
|
+
{
|
|
1135
|
+
name: "chrome_get_tool_graph",
|
|
1136
|
+
description: "CALL THIS FIRST before any task. Returns the optimal tool graph: which tools to use for a given intent, their cost, prerequisites, and which tools to AVOID to save tokens. Prevents redundant tool calls.",
|
|
1137
|
+
inputSchema: {
|
|
1138
|
+
type: "object",
|
|
1139
|
+
properties: {
|
|
1140
|
+
intent: { type: "string", description: "What you are trying to do in plain text e.g. 'read page content', 'click a button', 'capture network requests'" },
|
|
1141
|
+
tools: { type: "array", items: { type: "string" }, description: "Optional: filter graph to specific tool names" },
|
|
1142
|
+
},
|
|
1143
|
+
},
|
|
1144
|
+
},
|
|
1145
|
+
];
|
|
1146
|
+
// TOOL GRAPH (server-side, no extension needed)
|
|
1147
|
+
const TOOL_GRAPH = {
|
|
1148
|
+
// Observation (cheap, always prefer first)
|
|
1149
|
+
chrome_get_active_tab: { description: "Get current tab info", intent: ["what tab am I on", "current url", "current tab"], cost: "low", next: ["chrome_get_content", "chrome_get_page_info", "chrome_screenshot"] },
|
|
1150
|
+
chrome_list_tabs: { description: "List all open tabs", intent: ["how many tabs", "find tab", "list tabs", "all tabs open"], cost: "low", next: ["chrome_switch_tab", "chrome_close_tab"] },
|
|
1151
|
+
chrome_get_page_info: { description: "Title, URL, links, meta", intent: ["page title", "page url", "links on page", "meta description"], cost: "low", next: ["chrome_get_content", "chrome_find_elements"] },
|
|
1152
|
+
chrome_get_content: { description: "Full visible text of page", intent: ["read page", "page text", "what does page say", "extract text", "scrape"], cost: "low", avoid: ["chrome_get_html"] },
|
|
1153
|
+
chrome_get_workflow_context: { description: "Forms, buttons, inputs snapshot — use BEFORE interacting with a page", intent: ["plan interaction", "find form", "find button", "what inputs exist", "how to fill form"], cost: "low", next: ["chrome_click", "chrome_type", "chrome_find_accessible_nodes"] },
|
|
1154
|
+
chrome_find_elements: { description: "Find matching DOM elements", intent: ["find elements", "query selector", "list buttons", "list links"], cost: "low" },
|
|
1155
|
+
chrome_get_element_info: { description: "Info about one element", intent: ["is element visible", "element position", "element attributes"], cost: "low" },
|
|
1156
|
+
chrome_list_iframes: { description: "List all iframes", intent: ["iframes on page", "embedded frames"], cost: "low", next: ["chrome_switch_iframe"] },
|
|
1157
|
+
chrome_get_accessibility_tree: { description: "Full AX tree for finding by label/role", intent: ["find by label", "accessible name", "aria role"], cost: "medium", avoid: ["chrome_find_elements"] },
|
|
1158
|
+
chrome_find_accessible_nodes: { description: "Find AX node by name and role", intent: ["find button by label", "find input by name", "click button named"], cost: "medium", next: ["chrome_visual_click"] },
|
|
1159
|
+
chrome_ocr_page: { description: "All text with bounding boxes", intent: ["find text position", "where is text on screen"], cost: "medium", avoid: ["chrome_screenshot"] },
|
|
1160
|
+
chrome_find_text_on_screen: { description: "Find text and get click coordinates", intent: ["click text", "find text on screen", "where is button"], cost: "medium", next: ["chrome_visual_click"] },
|
|
1161
|
+
// Navigation (medium cost)
|
|
1162
|
+
chrome_navigate: { description: "Go to URL", intent: ["open url", "go to", "navigate to", "open website"], cost: "low", next: ["chrome_wait_for_element", "chrome_get_page_info"] },
|
|
1163
|
+
chrome_new_tab: { description: "Open new tab", intent: ["open new tab", "new tab", "open in new tab"], cost: "low" },
|
|
1164
|
+
chrome_switch_tab: { description: "Focus a tab by ID", intent: ["switch to tab", "go to tab"], cost: "low", requires: ["chrome_list_tabs"] },
|
|
1165
|
+
chrome_go_back: { description: "Browser back", intent: ["go back", "previous page"], cost: "low" },
|
|
1166
|
+
chrome_go_forward: { description: "Browser forward", intent: ["go forward", "next page"], cost: "low" },
|
|
1167
|
+
chrome_reload_tab: { description: "Reload tab", intent: ["reload", "refresh page"], cost: "low" },
|
|
1168
|
+
// Interaction (medium cost — always prefer get_workflow_context first)
|
|
1169
|
+
chrome_click: { description: "Click by CSS selector", intent: ["click button", "click link", "press button"], cost: "low", requires: ["chrome_get_workflow_context"], avoid: ["chrome_visual_click"] },
|
|
1170
|
+
chrome_type: { description: "Type into input by selector", intent: ["type text", "fill input", "enter text in field"], cost: "low", requires: ["chrome_get_workflow_context"] },
|
|
1171
|
+
chrome_key_press: { description: "Press a key (Enter, Escape)", intent: ["press enter", "press escape", "submit form"], cost: "low" },
|
|
1172
|
+
chrome_hover: { description: "Hover over element", intent: ["hover", "mouse over", "tooltip"], cost: "low" },
|
|
1173
|
+
chrome_select: { description: "Select dropdown option", intent: ["select option", "choose dropdown"], cost: "low" },
|
|
1174
|
+
chrome_scroll: { description: "Scroll page by pixels", intent: ["scroll down", "scroll up"], cost: "low" },
|
|
1175
|
+
chrome_wait_for_element: { description: "Wait for element to appear", intent: ["wait for load", "wait for element", "page loading"], cost: "low", next: ["chrome_click", "chrome_get_content"] },
|
|
1176
|
+
chrome_visual_click: { description: "Click by X/Y coordinates", intent: ["click coordinate", "click position"], cost: "medium", avoid: ["chrome_click"], requires: ["chrome_find_text_on_screen"] },
|
|
1177
|
+
chrome_upload_file: { description: "Set files on file input", intent: ["upload file", "attach file"], cost: "medium", requires: ["chrome_debug_attach"] },
|
|
1178
|
+
chrome_switch_iframe: { description: "Execute code inside iframe", intent: ["interact iframe", "iframe content"], cost: "medium", requires: ["chrome_list_iframes"] },
|
|
1179
|
+
// Screenshot (expensive — avoid unless visual check needed)
|
|
1180
|
+
chrome_screenshot: { description: "Screenshot of visible tab", intent: ["take screenshot", "capture page", "see page visually"], cost: "high", avoid: ["chrome_get_content", "chrome_get_page_info"] },
|
|
1181
|
+
// Debug tools (require attach first)
|
|
1182
|
+
chrome_debug_attach: { description: "Attach CDP debugger", intent: ["debug tab", "capture network", "read console", "monitor requests"], cost: "medium", next: ["chrome_debug_get_network", "chrome_debug_get_logs", "chrome_debug_get_performance"] },
|
|
1183
|
+
chrome_debug_get_logs: { description: "Read console logs", intent: ["console log", "js error", "read logs"], cost: "low", requires: ["chrome_debug_attach"] },
|
|
1184
|
+
chrome_debug_get_network: { description: "List captured network requests", intent: ["api calls", "network requests", "xhr", "fetch calls"], cost: "low", requires: ["chrome_debug_attach"] },
|
|
1185
|
+
chrome_debug_get_response_body: { description: "Read response body of request", intent: ["api response", "response body", "what did api return"], cost: "low", requires: ["chrome_debug_attach", "chrome_debug_get_network"] },
|
|
1186
|
+
chrome_debug_eval: { description: "Eval JS via CDP (async-safe)", intent: ["run async js", "await in page", "bypass csp"], cost: "medium", requires: ["chrome_debug_attach"], avoid: ["chrome_execute_script"] },
|
|
1187
|
+
chrome_debug_get_performance: { description: "JS heap, DOM node count, layout metrics", intent: ["performance", "memory usage", "page metrics"], cost: "medium", requires: ["chrome_debug_attach"] },
|
|
1188
|
+
chrome_debug_emulate_device: { description: "Mobile device emulation", intent: ["mobile view", "responsive test", "iphone view"], cost: "medium", requires: ["chrome_debug_attach"] },
|
|
1189
|
+
chrome_debug_emulate_network: { description: "Throttle network", intent: ["slow network", "offline test", "3g simulation"], cost: "medium", requires: ["chrome_debug_attach"] },
|
|
1190
|
+
chrome_debug_block_urls: { description: "Block URL patterns", intent: ["block ads", "block tracker", "block request"], cost: "medium", requires: ["chrome_debug_attach"] },
|
|
1191
|
+
chrome_debug_get_cookies: { description: "Get all cookies incl HttpOnly", intent: ["httponly cookie", "session cookie", "all cookies"], cost: "low", requires: ["chrome_debug_attach"], avoid: ["chrome_get_cookies"] },
|
|
1192
|
+
chrome_intercept_request: { description: "Intercept requests via CDP Fetch", intent: ["intercept api", "intercept request"], cost: "medium", requires: ["chrome_debug_attach"] },
|
|
1193
|
+
chrome_mock_response: { description: "Mock a URL response", intent: ["mock api", "fake response"], cost: "low", requires: ["chrome_intercept_request"] },
|
|
1194
|
+
chrome_modify_headers: { description: "Auto-modify request headers", intent: ["add auth header", "change user agent", "modify request"], cost: "medium", requires: ["chrome_debug_attach"] },
|
|
1195
|
+
chrome_export_har: { description: "Export HAR archive", intent: ["export network log", "har file"], cost: "low", requires: ["chrome_debug_attach", "chrome_debug_get_network"] },
|
|
1196
|
+
chrome_replay_request: { description: "Re-send HTTP request", intent: ["replay api", "resend request", "test api"], cost: "medium" },
|
|
1197
|
+
// Session
|
|
1198
|
+
chrome_save_session: { description: "Save cookies+localStorage", intent: ["save login", "save session", "bookmark session"], cost: "low" },
|
|
1199
|
+
chrome_restore_session: { description: "Restore saved session", intent: ["restore login", "load session"], cost: "low" },
|
|
1200
|
+
// Events / DOM watch
|
|
1201
|
+
chrome_subscribe_events: { description: "Listen to DOM events", intent: ["watch events", "detect clicks", "monitor input"], cost: "low" },
|
|
1202
|
+
chrome_watch_dom_changes: { description: "Watch DOM mutations", intent: ["watch dom", "detect changes", "mutation observer"], cost: "low" },
|
|
1203
|
+
// Permissions / Auth
|
|
1204
|
+
chrome_grant_permissions: { description: "Grant origin permissions", intent: ["allow camera", "allow location", "grant permission"], cost: "medium", requires: ["chrome_debug_attach"] },
|
|
1205
|
+
chrome_virtual_authenticator: { description: "WebAuthn virtual authenticator", intent: ["test passkey", "fido2 test", "webauthn"], cost: "medium", requires: ["chrome_debug_attach"] },
|
|
1206
|
+
// Storage
|
|
1207
|
+
chrome_get_cookies: { description: "Get cookies for URL", intent: ["get cookies", "cookie value"], cost: "low" },
|
|
1208
|
+
chrome_get_local_storage: { description: "Read localStorage", intent: ["localstorage", "local storage value"], cost: "low" },
|
|
1209
|
+
chrome_get_history: { description: "Search browser history", intent: ["browser history", "visited sites"], cost: "low" },
|
|
1210
|
+
};
|
|
1211
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
1212
|
+
return { tools: TOOLS };
|
|
1213
|
+
});
|
|
1214
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
1215
|
+
const { name, arguments: args } = request.params;
|
|
1216
|
+
// Tool Graph: handled server-side, no extension needed
|
|
1217
|
+
if (name === "chrome_get_tool_graph") {
|
|
1218
|
+
const intent = (args?.intent || "").toLowerCase();
|
|
1219
|
+
const filterTools = args?.tools;
|
|
1220
|
+
let graph = filterTools
|
|
1221
|
+
? Object.fromEntries(filterTools.map(t => [t, TOOL_GRAPH[t]]).filter(([, v]) => v))
|
|
1222
|
+
: TOOL_GRAPH;
|
|
1223
|
+
// If intent provided, score and rank by relevance
|
|
1224
|
+
if (intent) {
|
|
1225
|
+
const scored = Object.entries(graph)
|
|
1226
|
+
.map(([tool, info]) => {
|
|
1227
|
+
const typedInfo = info;
|
|
1228
|
+
const score = typedInfo.intent.reduce((s, kw) => s + (intent.includes(kw) ? 2 : kw.split(" ").some((w) => intent.includes(w)) ? 1 : 0), 0);
|
|
1229
|
+
return { tool, score, info: typedInfo };
|
|
1230
|
+
})
|
|
1231
|
+
.filter(e => e.score > 0)
|
|
1232
|
+
.sort((a, b) => b.score - a.score);
|
|
1233
|
+
const result = {
|
|
1234
|
+
intent,
|
|
1235
|
+
recommended: scored.slice(0, 5).map(e => ({
|
|
1236
|
+
tool: e.tool,
|
|
1237
|
+
description: e.info.description,
|
|
1238
|
+
cost: e.info.cost,
|
|
1239
|
+
requires: e.info.requires || [],
|
|
1240
|
+
next: e.info.next || [],
|
|
1241
|
+
avoid: e.info.avoid || [],
|
|
1242
|
+
})),
|
|
1243
|
+
avoid_these: scored.flatMap(e => e.info.avoid || []).filter((v, i, a) => a.indexOf(v) === i),
|
|
1244
|
+
execution_order: scored
|
|
1245
|
+
.slice(0, 5)
|
|
1246
|
+
.sort((a, b) => {
|
|
1247
|
+
const costs = { low: 0, medium: 1, high: 2 };
|
|
1248
|
+
return costs[a.info.cost] - costs[b.info.cost];
|
|
1249
|
+
})
|
|
1250
|
+
.map(e => e.tool),
|
|
1251
|
+
};
|
|
1252
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
1253
|
+
}
|
|
1254
|
+
// No intent — return full graph summary
|
|
1255
|
+
const summary = Object.entries(graph).map(([tool, info]) => {
|
|
1256
|
+
const typedInfo = info;
|
|
1257
|
+
return {
|
|
1258
|
+
tool,
|
|
1259
|
+
description: typedInfo.description,
|
|
1260
|
+
cost: typedInfo.cost,
|
|
1261
|
+
intent_keywords: typedInfo.intent,
|
|
1262
|
+
requires: typedInfo.requires || [],
|
|
1263
|
+
avoid: typedInfo.avoid || [],
|
|
1264
|
+
next: typedInfo.next || [],
|
|
1265
|
+
};
|
|
1266
|
+
});
|
|
1267
|
+
return { content: [{ type: "text", text: JSON.stringify(summary, null, 2) }] };
|
|
1268
|
+
}
|
|
1269
|
+
try {
|
|
1270
|
+
const method = name.replace(/^chrome_/, "");
|
|
1271
|
+
const result = await callExtension(method, args || {});
|
|
1272
|
+
return { content: [{ type: "text", text: String(result) }] };
|
|
1273
|
+
}
|
|
1274
|
+
catch (error) {
|
|
1275
|
+
return {
|
|
1276
|
+
content: [{ type: "text", text: `Error: ${error.message}` }],
|
|
1277
|
+
isError: true,
|
|
1278
|
+
};
|
|
1279
|
+
}
|
|
1280
|
+
});
|
|
1281
|
+
const transport = new StdioServerTransport();
|
|
1282
|
+
await server.connect(transport);
|
|
1283
|
+
console.error("MCP server connected via Stdio");
|
|
1284
|
+
process.on("exit", () => clearInterval(heartbeat));
|
|
1285
|
+
//# sourceMappingURL=index.js.map
|