@zenrows/mcp 2.0.3 → 2.0.5
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/dist/http.js +12 -1
- package/dist/server.d.ts +1 -1
- package/dist/server.js +10 -3
- package/dist/tools/browser-fetch.d.ts +1 -1
- package/dist/tools/browser-fetch.js +8 -2
- package/dist/tools/browser.d.ts +1 -1
- package/dist/tools/browser.js +40 -4
- package/package.json +1 -1
package/dist/http.js
CHANGED
|
@@ -5,6 +5,9 @@ import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/
|
|
|
5
5
|
import { createServer } from "./server.js";
|
|
6
6
|
// ─── app ──────────────────────────────────────────────────────────────────────
|
|
7
7
|
const app = new Hono();
|
|
8
|
+
// Cache MCP client name per API key across invocations.
|
|
9
|
+
// Populated from the clientInfo field in MCP initialize requests.
|
|
10
|
+
const mcpClientCache = new Map();
|
|
8
11
|
// CORS — required for browser-side MCP clients (Claude.ai, Cursor web, etc.)
|
|
9
12
|
// All origins allowed: the API key is the auth mechanism, not the origin.
|
|
10
13
|
app.use(cors({
|
|
@@ -93,9 +96,17 @@ app.all("/mcp", async (c) => {
|
|
|
93
96
|
sessionIdGenerator: undefined, // stateless — no session tracking between requests
|
|
94
97
|
enableJsonResponse: true, // return JSON instead of SSE (simpler for Lambda + most MCP clients)
|
|
95
98
|
});
|
|
96
|
-
|
|
99
|
+
// Use cached client name from a prior initialize request (stateless transport
|
|
100
|
+
// creates a new server per request, so getClientVersion() is only set during
|
|
101
|
+
// the initialize request itself, not during subsequent tool calls).
|
|
102
|
+
const server = createServer(apiKey, mcpClientCache.get(apiKey));
|
|
97
103
|
await server.connect(transport);
|
|
98
104
|
const response = await transport.handleRequest(c.req.raw);
|
|
105
|
+
// If this was an initialize request, getClientVersion() is now set — cache it
|
|
106
|
+
// so subsequent tool call requests can include x-mcp-client-name.
|
|
107
|
+
const resolvedName = server.server.getClientVersion()?.name;
|
|
108
|
+
if (resolvedName)
|
|
109
|
+
mcpClientCache.set(apiKey, resolvedName);
|
|
99
110
|
await transport.close();
|
|
100
111
|
return response;
|
|
101
112
|
});
|
package/dist/server.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
-
export declare function createServer(apiKey: string): McpServer;
|
|
2
|
+
export declare function createServer(apiKey: string, clientName?: string): McpServer;
|
package/dist/server.js
CHANGED
|
@@ -8,11 +8,14 @@ const ZENROWS_API_URL = "https://api.zenrows.com/v1/";
|
|
|
8
8
|
const DEFAULT_JS_RENDER = process.env.ZENROWS_JS_RENDER === "true";
|
|
9
9
|
const DEFAULT_PREMIUM_PROXY = process.env.ZENROWS_PREMIUM_PROXY === "true";
|
|
10
10
|
const DEFAULT_RESPONSE_TYPE = process.env.ZENROWS_RESPONSE_TYPE ?? "markdown";
|
|
11
|
-
export function createServer(apiKey) {
|
|
11
|
+
export function createServer(apiKey, clientName) {
|
|
12
12
|
const server = new McpServer({
|
|
13
13
|
name: "zenrows",
|
|
14
14
|
version: pkg.version,
|
|
15
15
|
});
|
|
16
|
+
// Resolve client name: prefer explicit value (HTTP stateless path),
|
|
17
|
+
// fall back to MCP handshake (stdio/persistent connection path).
|
|
18
|
+
const getClientName = () => clientName ?? server.server.getClientVersion()?.name;
|
|
16
19
|
// ─── scrape ────────────────────────────────────────────────────────────────
|
|
17
20
|
server.registerTool("scrape", {
|
|
18
21
|
annotations: {
|
|
@@ -164,7 +167,11 @@ Examples:
|
|
|
164
167
|
let response;
|
|
165
168
|
try {
|
|
166
169
|
response = await fetch(`${ZENROWS_API_URL}?${searchParams}`, {
|
|
167
|
-
headers: {
|
|
170
|
+
headers: {
|
|
171
|
+
"User-Agent": `zenrows/mcp ${pkg.version}`,
|
|
172
|
+
...(getClientName() ? { "x-mcp-client-name": getClientName() } : {}),
|
|
173
|
+
"x-mcp-tool": "scrape",
|
|
174
|
+
},
|
|
168
175
|
});
|
|
169
176
|
}
|
|
170
177
|
catch (err) {
|
|
@@ -261,6 +268,6 @@ Examples:
|
|
|
261
268
|
],
|
|
262
269
|
}));
|
|
263
270
|
const BROWSER_URL = process.env.ZENROWS_BROWSER_URL ?? "https://mcp.zenrows.com";
|
|
264
|
-
registerBrowserTools(server, apiKey, BROWSER_URL);
|
|
271
|
+
registerBrowserTools(server, apiKey, BROWSER_URL, getClientName);
|
|
265
272
|
return server;
|
|
266
273
|
}
|
|
@@ -3,5 +3,5 @@ export interface BrowserFetchResult {
|
|
|
3
3
|
status: number;
|
|
4
4
|
data: unknown;
|
|
5
5
|
}
|
|
6
|
-
export declare function browserFetch(method: string, path: string, apiKey: string, browserUrl: string, body?: unknown): Promise<BrowserFetchResult>;
|
|
6
|
+
export declare function browserFetch(method: string, path: string, apiKey: string, browserUrl: string, body?: unknown, clientName?: string, toolName?: string): Promise<BrowserFetchResult>;
|
|
7
7
|
export declare function browserError(result: BrowserFetchResult): string;
|
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
const REQUEST_TIMEOUT_MS = 35_000;
|
|
2
|
-
export async function browserFetch(method, path, apiKey, browserUrl, body) {
|
|
2
|
+
export async function browserFetch(method, path, apiKey, browserUrl, body, clientName, toolName) {
|
|
3
3
|
const url = `${browserUrl.replace(/\/$/, "")}${path}`;
|
|
4
|
+
const headers = {
|
|
5
|
+
Authorization: `Bearer ${apiKey}`,
|
|
6
|
+
"Content-Type": "application/json",
|
|
7
|
+
...(clientName ? { "x-mcp-client-name": clientName } : {}),
|
|
8
|
+
...(toolName ? { "x-mcp-tool": toolName } : {}),
|
|
9
|
+
};
|
|
4
10
|
const init = {
|
|
5
11
|
method,
|
|
6
|
-
headers
|
|
12
|
+
headers,
|
|
7
13
|
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
|
|
8
14
|
};
|
|
9
15
|
if (body !== undefined) {
|
package/dist/tools/browser.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
-
export declare function registerBrowserTools(server: McpServer, apiKey: string, browserUrl: string): void;
|
|
2
|
+
export declare function registerBrowserTools(server: McpServer, apiKey: string, browserUrl: string, getClientName: () => string | undefined): void;
|
package/dist/tools/browser.js
CHANGED
|
@@ -10,8 +10,8 @@ function json(data) {
|
|
|
10
10
|
return { content: [{ type: "text", text: JSON.stringify(data) }] };
|
|
11
11
|
}
|
|
12
12
|
const sessionId = z.string().describe("Session ID returned by browser_navigate");
|
|
13
|
-
export function registerBrowserTools(server, apiKey, browserUrl) {
|
|
14
|
-
const
|
|
13
|
+
export function registerBrowserTools(server, apiKey, browserUrl, getClientName) {
|
|
14
|
+
const tfetch = (toolName) => (method, path, body) => browserFetch(method, path, apiKey, browserUrl, body, getClientName(), toolName);
|
|
15
15
|
// ─── Session ─────────────────────────────────────────────────────────────
|
|
16
16
|
server.registerTool("browser_navigate", {
|
|
17
17
|
annotations: { title: "Open Browser & Navigate", readOnlyHint: false, destructiveHint: false },
|
|
@@ -35,6 +35,7 @@ When to use options:
|
|
|
35
35
|
proxy_region: z.string().optional().describe("World region code for geo-targeted proxy (eu=Europe, na=North America, ap=Asia Pacific, sa=South America, af=Africa, me=Middle East)"),
|
|
36
36
|
},
|
|
37
37
|
}, async (params) => {
|
|
38
|
+
const fetch = tfetch("browser_navigate");
|
|
38
39
|
let result;
|
|
39
40
|
try {
|
|
40
41
|
result = await fetch("POST", "/browser/sessions", {
|
|
@@ -69,6 +70,7 @@ When to use options:
|
|
|
69
70
|
description: "Close a browser session and free all associated resources. Always call this when done to release the browser slot.",
|
|
70
71
|
inputSchema: { session_id: sessionId },
|
|
71
72
|
}, async ({ session_id }) => {
|
|
73
|
+
const fetch = tfetch("browser_close");
|
|
72
74
|
try {
|
|
73
75
|
const result = await fetch("DELETE", `/browser/sessions/${session_id}`);
|
|
74
76
|
if (!result.ok)
|
|
@@ -85,6 +87,7 @@ When to use options:
|
|
|
85
87
|
description: "Navigate back to the previous page in the browser history.",
|
|
86
88
|
inputSchema: { session_id: sessionId },
|
|
87
89
|
}, async ({ session_id }) => {
|
|
90
|
+
const fetch = tfetch("browser_go_back");
|
|
88
91
|
try {
|
|
89
92
|
const result = await fetch("POST", `/browser/sessions/${session_id}/go_back`);
|
|
90
93
|
if (!result.ok)
|
|
@@ -100,6 +103,7 @@ When to use options:
|
|
|
100
103
|
description: "Navigate forward to the next page in the browser history.",
|
|
101
104
|
inputSchema: { session_id: sessionId },
|
|
102
105
|
}, async ({ session_id }) => {
|
|
106
|
+
const fetch = tfetch("browser_go_forward");
|
|
103
107
|
try {
|
|
104
108
|
const result = await fetch("POST", `/browser/sessions/${session_id}/go_forward`);
|
|
105
109
|
if (!result.ok)
|
|
@@ -115,6 +119,7 @@ When to use options:
|
|
|
115
119
|
description: "Reload the current page.",
|
|
116
120
|
inputSchema: { session_id: sessionId },
|
|
117
121
|
}, async ({ session_id }) => {
|
|
122
|
+
const fetch = tfetch("browser_reload");
|
|
118
123
|
try {
|
|
119
124
|
const result = await fetch("POST", `/browser/sessions/${session_id}/reload`);
|
|
120
125
|
if (!result.ok)
|
|
@@ -134,6 +139,7 @@ When to use options:
|
|
|
134
139
|
selector: z.string().describe("CSS selector of the element to click"),
|
|
135
140
|
},
|
|
136
141
|
}, async ({ session_id, selector }) => {
|
|
142
|
+
const fetch = tfetch("browser_click");
|
|
137
143
|
try {
|
|
138
144
|
const result = await fetch("POST", `/browser/sessions/${session_id}/click`, { selector });
|
|
139
145
|
if (!result.ok)
|
|
@@ -152,6 +158,7 @@ When to use options:
|
|
|
152
158
|
selector: z.string().describe("CSS selector of the element to hover over"),
|
|
153
159
|
},
|
|
154
160
|
}, async ({ session_id, selector }) => {
|
|
161
|
+
const fetch = tfetch("browser_hover");
|
|
155
162
|
try {
|
|
156
163
|
const result = await fetch("POST", `/browser/sessions/${session_id}/hover`, { selector });
|
|
157
164
|
if (!result.ok)
|
|
@@ -172,6 +179,7 @@ When to use options:
|
|
|
172
179
|
clear_first: z.boolean().optional().describe("Clear existing content before typing (default false)"),
|
|
173
180
|
},
|
|
174
181
|
}, async ({ session_id, selector, text, clear_first }) => {
|
|
182
|
+
const fetch = tfetch("browser_type");
|
|
175
183
|
try {
|
|
176
184
|
const result = await fetch("POST", `/browser/sessions/${session_id}/type`, { selector, text, clear_first });
|
|
177
185
|
if (!result.ok)
|
|
@@ -191,6 +199,7 @@ When to use options:
|
|
|
191
199
|
value: z.string().describe("Value to fill in"),
|
|
192
200
|
},
|
|
193
201
|
}, async ({ session_id, selector, value }) => {
|
|
202
|
+
const fetch = tfetch("browser_fill");
|
|
194
203
|
try {
|
|
195
204
|
const result = await fetch("POST", `/browser/sessions/${session_id}/fill`, { selector, value });
|
|
196
205
|
if (!result.ok)
|
|
@@ -210,6 +219,7 @@ When to use options:
|
|
|
210
219
|
value: z.string().describe("Value of the option to select"),
|
|
211
220
|
},
|
|
212
221
|
}, async ({ session_id, selector, value }) => {
|
|
222
|
+
const fetch = tfetch("browser_select_option");
|
|
213
223
|
try {
|
|
214
224
|
const result = await fetch("POST", `/browser/sessions/${session_id}/select`, { selector, value });
|
|
215
225
|
if (!result.ok)
|
|
@@ -228,6 +238,7 @@ When to use options:
|
|
|
228
238
|
selector: z.string().describe("CSS selector of the checkbox or radio element"),
|
|
229
239
|
},
|
|
230
240
|
}, async ({ session_id, selector }) => {
|
|
241
|
+
const fetch = tfetch("browser_check");
|
|
231
242
|
try {
|
|
232
243
|
const result = await fetch("POST", `/browser/sessions/${session_id}/check`, { selector });
|
|
233
244
|
if (!result.ok)
|
|
@@ -246,6 +257,7 @@ When to use options:
|
|
|
246
257
|
selector: z.string().describe("CSS selector of the checkbox element"),
|
|
247
258
|
},
|
|
248
259
|
}, async ({ session_id, selector }) => {
|
|
260
|
+
const fetch = tfetch("browser_uncheck");
|
|
249
261
|
try {
|
|
250
262
|
const result = await fetch("POST", `/browser/sessions/${session_id}/uncheck`, { selector });
|
|
251
263
|
if (!result.ok)
|
|
@@ -264,6 +276,7 @@ When to use options:
|
|
|
264
276
|
selector: z.string().describe("CSS selector of the element to focus"),
|
|
265
277
|
},
|
|
266
278
|
}, async ({ session_id, selector }) => {
|
|
279
|
+
const fetch = tfetch("browser_focus");
|
|
267
280
|
try {
|
|
268
281
|
const result = await fetch("POST", `/browser/sessions/${session_id}/focus`, { selector });
|
|
269
282
|
if (!result.ok)
|
|
@@ -282,6 +295,7 @@ When to use options:
|
|
|
282
295
|
key: z.string().describe('Key to press (e.g. "Enter", "Tab", "Escape", "ArrowDown", "Control+a")'),
|
|
283
296
|
},
|
|
284
297
|
}, async ({ session_id, key }) => {
|
|
298
|
+
const fetch = tfetch("browser_press_key");
|
|
285
299
|
try {
|
|
286
300
|
const result = await fetch("POST", `/browser/sessions/${session_id}/press_key`, { key });
|
|
287
301
|
if (!result.ok)
|
|
@@ -301,6 +315,7 @@ When to use options:
|
|
|
301
315
|
distance: z.number().int().positive().optional().describe("Pixels to scroll (default 500)"),
|
|
302
316
|
},
|
|
303
317
|
}, async ({ session_id, direction, distance }) => {
|
|
318
|
+
const fetch = tfetch("browser_scroll");
|
|
304
319
|
try {
|
|
305
320
|
const result = await fetch("POST", `/browser/sessions/${session_id}/scroll`, { direction, distance });
|
|
306
321
|
if (!result.ok)
|
|
@@ -320,6 +335,7 @@ When to use options:
|
|
|
320
335
|
target_selector: z.string().describe("CSS selector of the drop target"),
|
|
321
336
|
},
|
|
322
337
|
}, async ({ session_id, source_selector, target_selector }) => {
|
|
338
|
+
const fetch = tfetch("browser_drag");
|
|
323
339
|
try {
|
|
324
340
|
const result = await fetch("POST", `/browser/sessions/${session_id}/drag`, {
|
|
325
341
|
source_selector,
|
|
@@ -346,6 +362,7 @@ The tree shows all interactive elements (buttons, inputs, links) with their role
|
|
|
346
362
|
labels, and states — everything needed to drive browser interactions.`,
|
|
347
363
|
inputSchema: { session_id: sessionId },
|
|
348
364
|
}, async ({ session_id }) => {
|
|
365
|
+
const fetch = tfetch("browser_get_accessibility_tree");
|
|
349
366
|
try {
|
|
350
367
|
const result = await fetch("GET", `/browser/sessions/${session_id}/accessibility_tree`);
|
|
351
368
|
if (!result.ok)
|
|
@@ -362,6 +379,7 @@ labels, and states — everything needed to drive browser interactions.`,
|
|
|
362
379
|
description: "Get the current URL of the browser session.",
|
|
363
380
|
inputSchema: { session_id: sessionId },
|
|
364
381
|
}, async ({ session_id }) => {
|
|
382
|
+
const fetch = tfetch("browser_get_url");
|
|
365
383
|
try {
|
|
366
384
|
const result = await fetch("GET", `/browser/sessions/${session_id}/url`);
|
|
367
385
|
if (!result.ok)
|
|
@@ -377,6 +395,7 @@ labels, and states — everything needed to drive browser interactions.`,
|
|
|
377
395
|
description: "Get the title of the current page.",
|
|
378
396
|
inputSchema: { session_id: sessionId },
|
|
379
397
|
}, async ({ session_id }) => {
|
|
398
|
+
const fetch = tfetch("browser_get_title");
|
|
380
399
|
try {
|
|
381
400
|
const result = await fetch("GET", `/browser/sessions/${session_id}/title`);
|
|
382
401
|
if (!result.ok)
|
|
@@ -395,6 +414,7 @@ labels, and states — everything needed to drive browser interactions.`,
|
|
|
395
414
|
selector: z.string().optional().describe("CSS selector of the element (omit for full page body text)"),
|
|
396
415
|
},
|
|
397
416
|
}, async ({ session_id, selector }) => {
|
|
417
|
+
const fetch = tfetch("browser_get_text");
|
|
398
418
|
try {
|
|
399
419
|
const result = await fetch("POST", `/browser/sessions/${session_id}/get_text`, { selector });
|
|
400
420
|
if (!result.ok)
|
|
@@ -415,6 +435,7 @@ labels, and states — everything needed to drive browser interactions.`,
|
|
|
415
435
|
attribute: z.string().describe("Attribute name to retrieve (e.g. 'href', 'src', 'data-id')"),
|
|
416
436
|
},
|
|
417
437
|
}, async ({ session_id, selector, attribute }) => {
|
|
438
|
+
const fetch = tfetch("browser_get_attribute");
|
|
418
439
|
try {
|
|
419
440
|
const result = await fetch("POST", `/browser/sessions/${session_id}/get_attribute`, { selector, attribute });
|
|
420
441
|
if (!result.ok)
|
|
@@ -433,6 +454,7 @@ labels, and states — everything needed to drive browser interactions.`,
|
|
|
433
454
|
selector: z.string().optional().describe("CSS selector of the element (omit for full page HTML)"),
|
|
434
455
|
},
|
|
435
456
|
}, async ({ session_id, selector }) => {
|
|
457
|
+
const fetch = tfetch("browser_get_html");
|
|
436
458
|
try {
|
|
437
459
|
const result = await fetch("POST", `/browser/sessions/${session_id}/get_html`, { selector });
|
|
438
460
|
if (!result.ok)
|
|
@@ -452,6 +474,7 @@ labels, and states — everything needed to drive browser interactions.`,
|
|
|
452
474
|
selector: z.string().describe("CSS selector to query"),
|
|
453
475
|
},
|
|
454
476
|
}, async ({ session_id, selector }) => {
|
|
477
|
+
const fetch = tfetch("browser_query_selector_all");
|
|
455
478
|
try {
|
|
456
479
|
const result = await fetch("POST", `/browser/sessions/${session_id}/query_selector_all`, { selector });
|
|
457
480
|
if (!result.ok)
|
|
@@ -475,6 +498,7 @@ labels, and states — everything needed to drive browser interactions.`,
|
|
|
475
498
|
.describe("CSS selector to capture only a specific element (overrides full_page)"),
|
|
476
499
|
},
|
|
477
500
|
}, async ({ session_id, full_page, selector }) => {
|
|
501
|
+
const fetch = tfetch("browser_screenshot");
|
|
478
502
|
try {
|
|
479
503
|
const result = await fetch("POST", `/browser/sessions/${session_id}/screenshot`, { full_page, selector });
|
|
480
504
|
if (!result.ok)
|
|
@@ -504,6 +528,7 @@ labels, and states — everything needed to drive browser interactions.`,
|
|
|
504
528
|
scale: z.number().min(0.1).max(2).optional().describe("Page scale factor (default 1)"),
|
|
505
529
|
},
|
|
506
530
|
}, async ({ session_id, print_background, landscape, scale }) => {
|
|
531
|
+
const fetch = tfetch("browser_generate_pdf");
|
|
507
532
|
try {
|
|
508
533
|
const result = await fetch("POST", `/browser/sessions/${session_id}/generate_pdf`, { print_background, landscape, scale });
|
|
509
534
|
if (!result.ok)
|
|
@@ -536,6 +561,7 @@ labels, and states — everything needed to drive browser interactions.`,
|
|
|
536
561
|
visible: z.boolean().optional().describe("Also require the element to be visible, not just present in the DOM (default false)"),
|
|
537
562
|
},
|
|
538
563
|
}, async ({ session_id, selector, visible }) => {
|
|
564
|
+
const fetch = tfetch("browser_wait_for_selector");
|
|
539
565
|
try {
|
|
540
566
|
const result = await fetch("POST", `/browser/sessions/${session_id}/wait_for_selector`, { selector, visible });
|
|
541
567
|
if (!result.ok)
|
|
@@ -554,6 +580,7 @@ labels, and states — everything needed to drive browser interactions.`,
|
|
|
554
580
|
timeout_ms: z.number().int().min(1000).max(60000).optional().describe("How long to wait in milliseconds (default 30000, max 60000)"),
|
|
555
581
|
},
|
|
556
582
|
}, async ({ session_id, timeout_ms }) => {
|
|
583
|
+
const fetch = tfetch("browser_wait_for_navigation");
|
|
557
584
|
try {
|
|
558
585
|
const result = await fetch("POST", `/browser/sessions/${session_id}/wait_for_navigation`, timeout_ms ? { timeout_ms } : undefined);
|
|
559
586
|
if (!result.ok)
|
|
@@ -572,6 +599,7 @@ labels, and states — everything needed to drive browser interactions.`,
|
|
|
572
599
|
ms: z.number().int().min(0).max(30000).describe("Milliseconds to wait (max 30000)"),
|
|
573
600
|
},
|
|
574
601
|
}, async ({ session_id, ms }) => {
|
|
602
|
+
const fetch = tfetch("browser_wait");
|
|
575
603
|
try {
|
|
576
604
|
const result = await fetch("POST", `/browser/sessions/${session_id}/wait`, { ms });
|
|
577
605
|
if (!result.ok)
|
|
@@ -591,6 +619,7 @@ labels, and states — everything needed to drive browser interactions.`,
|
|
|
591
619
|
script: z.string().describe("JavaScript expression or function body to evaluate in the page context"),
|
|
592
620
|
},
|
|
593
621
|
}, async ({ session_id, script }) => {
|
|
622
|
+
const fetch = tfetch("browser_evaluate");
|
|
594
623
|
try {
|
|
595
624
|
const result = await fetch("POST", `/browser/sessions/${session_id}/evaluate`, { script });
|
|
596
625
|
if (!result.ok)
|
|
@@ -607,6 +636,7 @@ labels, and states — everything needed to drive browser interactions.`,
|
|
|
607
636
|
description: "Get all cookies for the current page.",
|
|
608
637
|
inputSchema: { session_id: sessionId },
|
|
609
638
|
}, async ({ session_id }) => {
|
|
639
|
+
const fetch = tfetch("browser_get_cookies");
|
|
610
640
|
try {
|
|
611
641
|
const result = await fetch("GET", `/browser/sessions/${session_id}/cookies`);
|
|
612
642
|
if (!result.ok)
|
|
@@ -635,6 +665,7 @@ labels, and states — everything needed to drive browser interactions.`,
|
|
|
635
665
|
.describe("Array of cookie objects to set"),
|
|
636
666
|
},
|
|
637
667
|
}, async ({ session_id, cookies }) => {
|
|
668
|
+
const fetch = tfetch("browser_set_cookies");
|
|
638
669
|
try {
|
|
639
670
|
// Remap http_only → httpOnly to match Chrome DevTools Protocol / Rod's JSON field names.
|
|
640
671
|
const mapped = cookies.map(({ http_only, ...rest }) => ({ ...rest, httpOnly: http_only }));
|
|
@@ -652,6 +683,7 @@ labels, and states — everything needed to drive browser interactions.`,
|
|
|
652
683
|
description: "Clear all cookies for the current browser session.",
|
|
653
684
|
inputSchema: { session_id: sessionId },
|
|
654
685
|
}, async ({ session_id }) => {
|
|
686
|
+
const fetch = tfetch("browser_clear_cookies");
|
|
655
687
|
try {
|
|
656
688
|
const result = await fetch("DELETE", `/browser/sessions/${session_id}/cookies`);
|
|
657
689
|
if (!result.ok)
|
|
@@ -673,6 +705,7 @@ labels, and states — everything needed to drive browser interactions.`,
|
|
|
673
705
|
value: z.string().optional().describe("Value to store (required for set)"),
|
|
674
706
|
},
|
|
675
707
|
}, async ({ session_id, action, key, value }) => {
|
|
708
|
+
const fetch = tfetch("browser_local_storage");
|
|
676
709
|
try {
|
|
677
710
|
const result = await fetch("POST", `/browser/sessions/${session_id}/local_storage`, { action, key, value });
|
|
678
711
|
if (!result.ok)
|
|
@@ -692,6 +725,7 @@ labels, and states — everything needed to drive browser interactions.`,
|
|
|
692
725
|
url: z.string().url().optional().describe("URL to open in the new tab (opens blank tab if omitted)"),
|
|
693
726
|
},
|
|
694
727
|
}, async ({ session_id, url }) => {
|
|
728
|
+
const fetch = tfetch("browser_new_tab");
|
|
695
729
|
try {
|
|
696
730
|
const result = await fetch("POST", `/browser/sessions/${session_id}/new_tab`, { url });
|
|
697
731
|
if (!result.ok)
|
|
@@ -710,6 +744,7 @@ labels, and states — everything needed to drive browser interactions.`,
|
|
|
710
744
|
tab_id: z.string().describe("Tab ID returned by browser_new_tab"),
|
|
711
745
|
},
|
|
712
746
|
}, async ({ session_id, tab_id }) => {
|
|
747
|
+
const fetch = tfetch("browser_switch_tab");
|
|
713
748
|
try {
|
|
714
749
|
const result = await fetch("POST", `/browser/sessions/${session_id}/switch_tab`, { tab_id });
|
|
715
750
|
if (!result.ok)
|
|
@@ -750,7 +785,7 @@ labels, and states — everything needed to drive browser interactions.`,
|
|
|
750
785
|
z.object({ type: z.literal("wait"), ms: z.number().int().min(0).max(30000) }),
|
|
751
786
|
z.object({ type: z.literal("evaluate"), script: z.string() }),
|
|
752
787
|
]);
|
|
753
|
-
async function runAction(action, session_id) {
|
|
788
|
+
async function runAction(action, session_id, fetch) {
|
|
754
789
|
const sid = session_id;
|
|
755
790
|
switch (action.type) {
|
|
756
791
|
case "navigate": {
|
|
@@ -938,11 +973,12 @@ For proper image rendering, use browser_screenshot directly.`,
|
|
|
938
973
|
stop_on_error: z.boolean().optional().describe("Stop executing on the first failed action (default true)"),
|
|
939
974
|
},
|
|
940
975
|
}, async ({ session_id, actions, stop_on_error = true }) => {
|
|
976
|
+
const fetch = tfetch("browser_batch");
|
|
941
977
|
const results = [];
|
|
942
978
|
for (let i = 0; i < actions.length; i++) {
|
|
943
979
|
const action = actions[i];
|
|
944
980
|
try {
|
|
945
|
-
const result = await runAction(action, session_id);
|
|
981
|
+
const result = await runAction(action, session_id, fetch);
|
|
946
982
|
results.push({ step: i, type: action.type, result });
|
|
947
983
|
}
|
|
948
984
|
catch (e) {
|