@zenrows/mcp 2.1.2 → 2.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -7
- package/dist/auth/claim-hint.d.ts +16 -0
- package/dist/auth/claim-hint.js +71 -0
- package/dist/auth/ensure-key.d.ts +59 -0
- package/dist/auth/ensure-key.js +184 -0
- package/dist/batch-api.d.ts +89 -0
- package/dist/batch-api.js +182 -0
- package/dist/http.js +4 -4
- package/dist/index.js +30 -4
- package/dist/server.js +51 -15
- package/dist/tools/account.d.ts +18 -0
- package/dist/tools/account.js +98 -0
- package/dist/tools/batch.d.ts +2 -0
- package/dist/tools/batch.js +245 -0
- package/dist/tools/browser.js +194 -47
- package/dist/tools/extract.d.ts +40 -0
- package/dist/tools/extract.js +232 -0
- package/package.json +6 -3
package/dist/tools/browser.js
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { appendClaimHint } from "../auth/claim-hint.js";
|
|
2
3
|
import { browserFetch, browserError } from "./browser-fetch.js";
|
|
3
4
|
function ok() {
|
|
4
5
|
return { content: [{ type: "text", text: JSON.stringify({ ok: true }) }] };
|
|
5
6
|
}
|
|
6
7
|
function err(msg) {
|
|
7
|
-
return {
|
|
8
|
+
return {
|
|
9
|
+
content: [{ type: "text", text: appendClaimHint(msg, { body: msg, message: msg }) }],
|
|
10
|
+
isError: true,
|
|
11
|
+
};
|
|
8
12
|
}
|
|
9
13
|
function json(data) {
|
|
10
14
|
return { content: [{ type: "text", text: JSON.stringify(data) }] };
|
|
@@ -14,7 +18,11 @@ export function registerBrowserTools(server, apiKey, browserUrl, getClientName)
|
|
|
14
18
|
const tfetch = (toolName) => (method, path, body) => browserFetch(method, path, apiKey, browserUrl, body, getClientName(), toolName);
|
|
15
19
|
// ─── Session ─────────────────────────────────────────────────────────────
|
|
16
20
|
server.registerTool("browser_navigate", {
|
|
17
|
-
annotations: {
|
|
21
|
+
annotations: {
|
|
22
|
+
title: "Open Browser & Navigate",
|
|
23
|
+
readOnlyHint: false,
|
|
24
|
+
destructiveHint: false,
|
|
25
|
+
},
|
|
18
26
|
description: `Open a Zenrows Browser Sessions session and navigate to a URL.
|
|
19
27
|
|
|
20
28
|
This is the entry point for all browser automation. It creates a new session backed by
|
|
@@ -32,7 +40,10 @@ When to use options:
|
|
|
32
40
|
.string()
|
|
33
41
|
.optional()
|
|
34
42
|
.describe("ISO 3166-1 alpha-2 country code for geo-targeted proxy (e.g. 'US', 'GB', 'DE')"),
|
|
35
|
-
proxy_region: z
|
|
43
|
+
proxy_region: z
|
|
44
|
+
.string()
|
|
45
|
+
.optional()
|
|
46
|
+
.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
47
|
},
|
|
37
48
|
}, async (params) => {
|
|
38
49
|
const fetch = tfetch("browser_navigate");
|
|
@@ -51,7 +62,9 @@ When to use options:
|
|
|
51
62
|
const session = result.data;
|
|
52
63
|
let navResult;
|
|
53
64
|
try {
|
|
54
|
-
navResult = await fetch("POST", `/browser/sessions/${session.session_id}/navigate`, {
|
|
65
|
+
navResult = await fetch("POST", `/browser/sessions/${session.session_id}/navigate`, {
|
|
66
|
+
url: params.url,
|
|
67
|
+
});
|
|
55
68
|
}
|
|
56
69
|
catch (e) {
|
|
57
70
|
// Best-effort cleanup — session was created but navigation failed, free the slot.
|
|
@@ -63,7 +76,12 @@ When to use options:
|
|
|
63
76
|
return err(`Navigation failed: ${browserError(navResult)}`);
|
|
64
77
|
}
|
|
65
78
|
const nav = navResult.data;
|
|
66
|
-
return json({
|
|
79
|
+
return json({
|
|
80
|
+
session_id: session.session_id,
|
|
81
|
+
url: nav.url,
|
|
82
|
+
title: nav.title,
|
|
83
|
+
expires_at: session.expires_at,
|
|
84
|
+
});
|
|
67
85
|
});
|
|
68
86
|
server.registerTool("browser_close", {
|
|
69
87
|
annotations: { title: "Close Browser Session", readOnlyHint: false, destructiveHint: false },
|
|
@@ -181,7 +199,11 @@ When to use options:
|
|
|
181
199
|
}, async ({ session_id, selector, text, clear_first }) => {
|
|
182
200
|
const fetch = tfetch("browser_type");
|
|
183
201
|
try {
|
|
184
|
-
const result = await fetch("POST", `/browser/sessions/${session_id}/type`, {
|
|
202
|
+
const result = await fetch("POST", `/browser/sessions/${session_id}/type`, {
|
|
203
|
+
selector,
|
|
204
|
+
text,
|
|
205
|
+
clear_first,
|
|
206
|
+
});
|
|
185
207
|
if (!result.ok)
|
|
186
208
|
return err(`Type failed: ${browserError(result)}`);
|
|
187
209
|
return ok();
|
|
@@ -201,7 +223,10 @@ When to use options:
|
|
|
201
223
|
}, async ({ session_id, selector, value }) => {
|
|
202
224
|
const fetch = tfetch("browser_fill");
|
|
203
225
|
try {
|
|
204
|
-
const result = await fetch("POST", `/browser/sessions/${session_id}/fill`, {
|
|
226
|
+
const result = await fetch("POST", `/browser/sessions/${session_id}/fill`, {
|
|
227
|
+
selector,
|
|
228
|
+
value,
|
|
229
|
+
});
|
|
205
230
|
if (!result.ok)
|
|
206
231
|
return err(`Fill failed: ${browserError(result)}`);
|
|
207
232
|
return ok();
|
|
@@ -221,7 +246,10 @@ When to use options:
|
|
|
221
246
|
}, async ({ session_id, selector, value }) => {
|
|
222
247
|
const fetch = tfetch("browser_select_option");
|
|
223
248
|
try {
|
|
224
|
-
const result = await fetch("POST", `/browser/sessions/${session_id}/select`, {
|
|
249
|
+
const result = await fetch("POST", `/browser/sessions/${session_id}/select`, {
|
|
250
|
+
selector,
|
|
251
|
+
value,
|
|
252
|
+
});
|
|
225
253
|
if (!result.ok)
|
|
226
254
|
return err(`Select failed: ${browserError(result)}`);
|
|
227
255
|
return ok();
|
|
@@ -317,7 +345,10 @@ When to use options:
|
|
|
317
345
|
}, async ({ session_id, direction, distance }) => {
|
|
318
346
|
const fetch = tfetch("browser_scroll");
|
|
319
347
|
try {
|
|
320
|
-
const result = await fetch("POST", `/browser/sessions/${session_id}/scroll`, {
|
|
348
|
+
const result = await fetch("POST", `/browser/sessions/${session_id}/scroll`, {
|
|
349
|
+
direction,
|
|
350
|
+
distance,
|
|
351
|
+
});
|
|
321
352
|
if (!result.ok)
|
|
322
353
|
return err(`Scroll failed: ${browserError(result)}`);
|
|
323
354
|
return ok();
|
|
@@ -416,7 +447,9 @@ labels, and states — everything needed to drive browser interactions.`,
|
|
|
416
447
|
}, async ({ session_id, selector }) => {
|
|
417
448
|
const fetch = tfetch("browser_get_text");
|
|
418
449
|
try {
|
|
419
|
-
const result = await fetch("POST", `/browser/sessions/${session_id}/get_text`, {
|
|
450
|
+
const result = await fetch("POST", `/browser/sessions/${session_id}/get_text`, {
|
|
451
|
+
selector,
|
|
452
|
+
});
|
|
420
453
|
if (!result.ok)
|
|
421
454
|
return err(`Failed to get text: ${browserError(result)}`);
|
|
422
455
|
const data = result.data;
|
|
@@ -437,7 +470,10 @@ labels, and states — everything needed to drive browser interactions.`,
|
|
|
437
470
|
}, async ({ session_id, selector, attribute }) => {
|
|
438
471
|
const fetch = tfetch("browser_get_attribute");
|
|
439
472
|
try {
|
|
440
|
-
const result = await fetch("POST", `/browser/sessions/${session_id}/get_attribute`, {
|
|
473
|
+
const result = await fetch("POST", `/browser/sessions/${session_id}/get_attribute`, {
|
|
474
|
+
selector,
|
|
475
|
+
attribute,
|
|
476
|
+
});
|
|
441
477
|
if (!result.ok)
|
|
442
478
|
return err(`Failed to get attribute: ${browserError(result)}`);
|
|
443
479
|
return json(result.data);
|
|
@@ -456,7 +492,9 @@ labels, and states — everything needed to drive browser interactions.`,
|
|
|
456
492
|
}, async ({ session_id, selector }) => {
|
|
457
493
|
const fetch = tfetch("browser_get_html");
|
|
458
494
|
try {
|
|
459
|
-
const result = await fetch("POST", `/browser/sessions/${session_id}/get_html`, {
|
|
495
|
+
const result = await fetch("POST", `/browser/sessions/${session_id}/get_html`, {
|
|
496
|
+
selector,
|
|
497
|
+
});
|
|
460
498
|
if (!result.ok)
|
|
461
499
|
return err(`Failed to get HTML: ${browserError(result)}`);
|
|
462
500
|
const data = result.data;
|
|
@@ -467,7 +505,11 @@ labels, and states — everything needed to drive browser interactions.`,
|
|
|
467
505
|
}
|
|
468
506
|
});
|
|
469
507
|
server.registerTool("browser_query_selector_all", {
|
|
470
|
-
annotations: {
|
|
508
|
+
annotations: {
|
|
509
|
+
title: "Query All Matching Elements",
|
|
510
|
+
readOnlyHint: true,
|
|
511
|
+
destructiveHint: false,
|
|
512
|
+
},
|
|
471
513
|
description: "Find all elements matching a CSS selector and return their text, HTML, and attributes.",
|
|
472
514
|
inputSchema: {
|
|
473
515
|
session_id: sessionId,
|
|
@@ -476,7 +518,9 @@ labels, and states — everything needed to drive browser interactions.`,
|
|
|
476
518
|
}, async ({ session_id, selector }) => {
|
|
477
519
|
const fetch = tfetch("browser_query_selector_all");
|
|
478
520
|
try {
|
|
479
|
-
const result = await fetch("POST", `/browser/sessions/${session_id}/query_selector_all`, {
|
|
521
|
+
const result = await fetch("POST", `/browser/sessions/${session_id}/query_selector_all`, {
|
|
522
|
+
selector,
|
|
523
|
+
});
|
|
480
524
|
if (!result.ok)
|
|
481
525
|
return err(`Query failed: ${browserError(result)}`);
|
|
482
526
|
return json(result.data);
|
|
@@ -491,7 +535,10 @@ labels, and states — everything needed to drive browser interactions.`,
|
|
|
491
535
|
description: "Take a screenshot of the current page or a specific element. Use for visual verification or when the accessibility tree is not sufficient.",
|
|
492
536
|
inputSchema: {
|
|
493
537
|
session_id: sessionId,
|
|
494
|
-
full_page: z
|
|
538
|
+
full_page: z
|
|
539
|
+
.boolean()
|
|
540
|
+
.optional()
|
|
541
|
+
.describe("Capture full page including content below the fold (default false)"),
|
|
495
542
|
selector: z
|
|
496
543
|
.string()
|
|
497
544
|
.optional()
|
|
@@ -500,7 +547,10 @@ labels, and states — everything needed to drive browser interactions.`,
|
|
|
500
547
|
}, async ({ session_id, full_page, selector }) => {
|
|
501
548
|
const fetch = tfetch("browser_screenshot");
|
|
502
549
|
try {
|
|
503
|
-
const result = await fetch("POST", `/browser/sessions/${session_id}/screenshot`, {
|
|
550
|
+
const result = await fetch("POST", `/browser/sessions/${session_id}/screenshot`, {
|
|
551
|
+
full_page,
|
|
552
|
+
selector,
|
|
553
|
+
});
|
|
504
554
|
if (!result.ok)
|
|
505
555
|
return err(`Screenshot failed: ${browserError(result)}`);
|
|
506
556
|
const data = result.data;
|
|
@@ -530,7 +580,11 @@ labels, and states — everything needed to drive browser interactions.`,
|
|
|
530
580
|
}, async ({ session_id, print_background, landscape, scale }) => {
|
|
531
581
|
const fetch = tfetch("browser_generate_pdf");
|
|
532
582
|
try {
|
|
533
|
-
const result = await fetch("POST", `/browser/sessions/${session_id}/generate_pdf`, {
|
|
583
|
+
const result = await fetch("POST", `/browser/sessions/${session_id}/generate_pdf`, {
|
|
584
|
+
print_background,
|
|
585
|
+
landscape,
|
|
586
|
+
scale,
|
|
587
|
+
});
|
|
534
588
|
if (!result.ok)
|
|
535
589
|
return err(`PDF generation failed: ${browserError(result)}`);
|
|
536
590
|
const data = result.data;
|
|
@@ -558,12 +612,18 @@ labels, and states — everything needed to drive browser interactions.`,
|
|
|
558
612
|
inputSchema: {
|
|
559
613
|
session_id: sessionId,
|
|
560
614
|
selector: z.string().describe("CSS selector to wait for"),
|
|
561
|
-
visible: z
|
|
615
|
+
visible: z
|
|
616
|
+
.boolean()
|
|
617
|
+
.optional()
|
|
618
|
+
.describe("Also require the element to be visible, not just present in the DOM (default false)"),
|
|
562
619
|
},
|
|
563
620
|
}, async ({ session_id, selector, visible }) => {
|
|
564
621
|
const fetch = tfetch("browser_wait_for_selector");
|
|
565
622
|
try {
|
|
566
|
-
const result = await fetch("POST", `/browser/sessions/${session_id}/wait_for_selector`, {
|
|
623
|
+
const result = await fetch("POST", `/browser/sessions/${session_id}/wait_for_selector`, {
|
|
624
|
+
selector,
|
|
625
|
+
visible,
|
|
626
|
+
});
|
|
567
627
|
if (!result.ok)
|
|
568
628
|
return err(`Wait for selector failed: ${browserError(result)}`);
|
|
569
629
|
return ok();
|
|
@@ -577,7 +637,13 @@ labels, and states — everything needed to drive browser interactions.`,
|
|
|
577
637
|
description: "Wait for the page to navigate to a new URL. IMPORTANT: call this BEFORE the action that triggers navigation (e.g. before browser_click on a submit button), not after — the navigation event may already have fired and this will hang until timeout. If the page stays on the same URL (AJAX/SPA), skip this tool entirely. Optional timeout_ms (default 30000ms).",
|
|
578
638
|
inputSchema: {
|
|
579
639
|
session_id: sessionId,
|
|
580
|
-
timeout_ms: z
|
|
640
|
+
timeout_ms: z
|
|
641
|
+
.number()
|
|
642
|
+
.int()
|
|
643
|
+
.min(1000)
|
|
644
|
+
.max(60000)
|
|
645
|
+
.optional()
|
|
646
|
+
.describe("How long to wait in milliseconds (default 30000, max 60000)"),
|
|
581
647
|
},
|
|
582
648
|
}, async ({ session_id, timeout_ms }) => {
|
|
583
649
|
const fetch = tfetch("browser_wait_for_navigation");
|
|
@@ -669,7 +735,9 @@ labels, and states — everything needed to drive browser interactions.`,
|
|
|
669
735
|
try {
|
|
670
736
|
// Remap http_only → httpOnly to match Chrome DevTools Protocol / Rod's JSON field names.
|
|
671
737
|
const mapped = cookies.map(({ http_only, ...rest }) => ({ ...rest, httpOnly: http_only }));
|
|
672
|
-
const result = await fetch("POST", `/browser/sessions/${session_id}/cookies`, {
|
|
738
|
+
const result = await fetch("POST", `/browser/sessions/${session_id}/cookies`, {
|
|
739
|
+
cookies: mapped,
|
|
740
|
+
});
|
|
673
741
|
if (!result.ok)
|
|
674
742
|
return err(`Failed to set cookies: ${browserError(result)}`);
|
|
675
743
|
return ok();
|
|
@@ -707,7 +775,11 @@ labels, and states — everything needed to drive browser interactions.`,
|
|
|
707
775
|
}, async ({ session_id, action, key, value }) => {
|
|
708
776
|
const fetch = tfetch("browser_local_storage");
|
|
709
777
|
try {
|
|
710
|
-
const result = await fetch("POST", `/browser/sessions/${session_id}/local_storage`, {
|
|
778
|
+
const result = await fetch("POST", `/browser/sessions/${session_id}/local_storage`, {
|
|
779
|
+
action,
|
|
780
|
+
key,
|
|
781
|
+
value,
|
|
782
|
+
});
|
|
711
783
|
if (!result.ok)
|
|
712
784
|
return err(`Local storage operation failed: ${browserError(result)}`);
|
|
713
785
|
return json(result.data);
|
|
@@ -746,7 +818,9 @@ labels, and states — everything needed to drive browser interactions.`,
|
|
|
746
818
|
}, async ({ session_id, tab_id }) => {
|
|
747
819
|
const fetch = tfetch("browser_switch_tab");
|
|
748
820
|
try {
|
|
749
|
-
const result = await fetch("POST", `/browser/sessions/${session_id}/switch_tab`, {
|
|
821
|
+
const result = await fetch("POST", `/browser/sessions/${session_id}/switch_tab`, {
|
|
822
|
+
tab_id,
|
|
823
|
+
});
|
|
750
824
|
if (!result.ok)
|
|
751
825
|
return err(`Failed to switch tab: ${browserError(result)}`);
|
|
752
826
|
return ok();
|
|
@@ -763,14 +837,23 @@ labels, and states — everything needed to drive browser interactions.`,
|
|
|
763
837
|
z.object({ type: z.literal("reload") }),
|
|
764
838
|
z.object({ type: z.literal("click"), selector: z.string() }),
|
|
765
839
|
z.object({ type: z.literal("hover"), selector: z.string() }),
|
|
766
|
-
z.object({
|
|
840
|
+
z.object({
|
|
841
|
+
type: z.literal("type"),
|
|
842
|
+
selector: z.string(),
|
|
843
|
+
text: z.string(),
|
|
844
|
+
clear_first: z.boolean().optional(),
|
|
845
|
+
}),
|
|
767
846
|
z.object({ type: z.literal("fill"), selector: z.string(), value: z.string() }),
|
|
768
847
|
z.object({ type: z.literal("select"), selector: z.string(), value: z.string() }),
|
|
769
848
|
z.object({ type: z.literal("check"), selector: z.string() }),
|
|
770
849
|
z.object({ type: z.literal("uncheck"), selector: z.string() }),
|
|
771
850
|
z.object({ type: z.literal("focus"), selector: z.string() }),
|
|
772
851
|
z.object({ type: z.literal("press_key"), key: z.string() }),
|
|
773
|
-
z.object({
|
|
852
|
+
z.object({
|
|
853
|
+
type: z.literal("scroll"),
|
|
854
|
+
direction: z.enum(["up", "down", "left", "right"]),
|
|
855
|
+
distance: z.number().optional(),
|
|
856
|
+
}),
|
|
774
857
|
z.object({ type: z.literal("drag"), source_selector: z.string(), target_selector: z.string() }),
|
|
775
858
|
z.object({ type: z.literal("get_accessibility_tree") }),
|
|
776
859
|
z.object({ type: z.literal("get_url") }),
|
|
@@ -779,8 +862,16 @@ labels, and states — everything needed to drive browser interactions.`,
|
|
|
779
862
|
z.object({ type: z.literal("get_attribute"), selector: z.string(), attribute: z.string() }),
|
|
780
863
|
z.object({ type: z.literal("get_html"), selector: z.string().optional() }),
|
|
781
864
|
z.object({ type: z.literal("query_selector_all"), selector: z.string() }),
|
|
782
|
-
z.object({
|
|
783
|
-
|
|
865
|
+
z.object({
|
|
866
|
+
type: z.literal("screenshot"),
|
|
867
|
+
full_page: z.boolean().optional(),
|
|
868
|
+
selector: z.string().optional(),
|
|
869
|
+
}),
|
|
870
|
+
z.object({
|
|
871
|
+
type: z.literal("wait_for_selector"),
|
|
872
|
+
selector: z.string(),
|
|
873
|
+
visible: z.boolean().optional(),
|
|
874
|
+
}),
|
|
784
875
|
z.object({ type: z.literal("wait_for_navigation") }),
|
|
785
876
|
z.object({ type: z.literal("wait"), ms: z.number().int().min(0).max(30000) }),
|
|
786
877
|
z.object({ type: z.literal("evaluate"), script: z.string() }),
|
|
@@ -813,49 +904,69 @@ labels, and states — everything needed to drive browser interactions.`,
|
|
|
813
904
|
return r.data;
|
|
814
905
|
}
|
|
815
906
|
case "click": {
|
|
816
|
-
const r = await fetch("POST", `/browser/sessions/${sid}/click`, {
|
|
907
|
+
const r = await fetch("POST", `/browser/sessions/${sid}/click`, {
|
|
908
|
+
selector: action.selector,
|
|
909
|
+
});
|
|
817
910
|
if (!r.ok)
|
|
818
911
|
throw new Error(browserError(r));
|
|
819
912
|
return { ok: true };
|
|
820
913
|
}
|
|
821
914
|
case "hover": {
|
|
822
|
-
const r = await fetch("POST", `/browser/sessions/${sid}/hover`, {
|
|
915
|
+
const r = await fetch("POST", `/browser/sessions/${sid}/hover`, {
|
|
916
|
+
selector: action.selector,
|
|
917
|
+
});
|
|
823
918
|
if (!r.ok)
|
|
824
919
|
throw new Error(browserError(r));
|
|
825
920
|
return { ok: true };
|
|
826
921
|
}
|
|
827
922
|
case "type": {
|
|
828
|
-
const r = await fetch("POST", `/browser/sessions/${sid}/type`, {
|
|
923
|
+
const r = await fetch("POST", `/browser/sessions/${sid}/type`, {
|
|
924
|
+
selector: action.selector,
|
|
925
|
+
text: action.text,
|
|
926
|
+
clear_first: action.clear_first,
|
|
927
|
+
});
|
|
829
928
|
if (!r.ok)
|
|
830
929
|
throw new Error(browserError(r));
|
|
831
930
|
return { ok: true };
|
|
832
931
|
}
|
|
833
932
|
case "fill": {
|
|
834
|
-
const r = await fetch("POST", `/browser/sessions/${sid}/fill`, {
|
|
933
|
+
const r = await fetch("POST", `/browser/sessions/${sid}/fill`, {
|
|
934
|
+
selector: action.selector,
|
|
935
|
+
value: action.value,
|
|
936
|
+
});
|
|
835
937
|
if (!r.ok)
|
|
836
938
|
throw new Error(browserError(r));
|
|
837
939
|
return { ok: true };
|
|
838
940
|
}
|
|
839
941
|
case "select": {
|
|
840
|
-
const r = await fetch("POST", `/browser/sessions/${sid}/select`, {
|
|
942
|
+
const r = await fetch("POST", `/browser/sessions/${sid}/select`, {
|
|
943
|
+
selector: action.selector,
|
|
944
|
+
value: action.value,
|
|
945
|
+
});
|
|
841
946
|
if (!r.ok)
|
|
842
947
|
throw new Error(browserError(r));
|
|
843
948
|
return { ok: true };
|
|
844
949
|
}
|
|
845
950
|
case "check": {
|
|
846
|
-
const r = await fetch("POST", `/browser/sessions/${sid}/check`, {
|
|
951
|
+
const r = await fetch("POST", `/browser/sessions/${sid}/check`, {
|
|
952
|
+
selector: action.selector,
|
|
953
|
+
});
|
|
847
954
|
if (!r.ok)
|
|
848
955
|
throw new Error(browserError(r));
|
|
849
956
|
return { ok: true };
|
|
850
957
|
}
|
|
851
958
|
case "uncheck": {
|
|
852
|
-
const r = await fetch("POST", `/browser/sessions/${sid}/uncheck`, {
|
|
959
|
+
const r = await fetch("POST", `/browser/sessions/${sid}/uncheck`, {
|
|
960
|
+
selector: action.selector,
|
|
961
|
+
});
|
|
853
962
|
if (!r.ok)
|
|
854
963
|
throw new Error(browserError(r));
|
|
855
964
|
return { ok: true };
|
|
856
965
|
}
|
|
857
966
|
case "focus": {
|
|
858
|
-
const r = await fetch("POST", `/browser/sessions/${sid}/focus`, {
|
|
967
|
+
const r = await fetch("POST", `/browser/sessions/${sid}/focus`, {
|
|
968
|
+
selector: action.selector,
|
|
969
|
+
});
|
|
859
970
|
if (!r.ok)
|
|
860
971
|
throw new Error(browserError(r));
|
|
861
972
|
return { ok: true };
|
|
@@ -867,13 +978,19 @@ labels, and states — everything needed to drive browser interactions.`,
|
|
|
867
978
|
return { ok: true };
|
|
868
979
|
}
|
|
869
980
|
case "scroll": {
|
|
870
|
-
const r = await fetch("POST", `/browser/sessions/${sid}/scroll`, {
|
|
981
|
+
const r = await fetch("POST", `/browser/sessions/${sid}/scroll`, {
|
|
982
|
+
direction: action.direction,
|
|
983
|
+
distance: action.distance,
|
|
984
|
+
});
|
|
871
985
|
if (!r.ok)
|
|
872
986
|
throw new Error(browserError(r));
|
|
873
987
|
return { ok: true };
|
|
874
988
|
}
|
|
875
989
|
case "drag": {
|
|
876
|
-
const r = await fetch("POST", `/browser/sessions/${sid}/drag`, {
|
|
990
|
+
const r = await fetch("POST", `/browser/sessions/${sid}/drag`, {
|
|
991
|
+
source_selector: action.source_selector,
|
|
992
|
+
target_selector: action.target_selector,
|
|
993
|
+
});
|
|
877
994
|
if (!r.ok)
|
|
878
995
|
throw new Error(browserError(r));
|
|
879
996
|
return { ok: true };
|
|
@@ -897,31 +1014,43 @@ labels, and states — everything needed to drive browser interactions.`,
|
|
|
897
1014
|
return r.data;
|
|
898
1015
|
}
|
|
899
1016
|
case "get_text": {
|
|
900
|
-
const r = await fetch("POST", `/browser/sessions/${sid}/get_text`, {
|
|
1017
|
+
const r = await fetch("POST", `/browser/sessions/${sid}/get_text`, {
|
|
1018
|
+
selector: action.selector,
|
|
1019
|
+
});
|
|
901
1020
|
if (!r.ok)
|
|
902
1021
|
throw new Error(browserError(r));
|
|
903
1022
|
return r.data.text;
|
|
904
1023
|
}
|
|
905
1024
|
case "get_attribute": {
|
|
906
|
-
const r = await fetch("POST", `/browser/sessions/${sid}/get_attribute`, {
|
|
1025
|
+
const r = await fetch("POST", `/browser/sessions/${sid}/get_attribute`, {
|
|
1026
|
+
selector: action.selector,
|
|
1027
|
+
attribute: action.attribute,
|
|
1028
|
+
});
|
|
907
1029
|
if (!r.ok)
|
|
908
1030
|
throw new Error(browserError(r));
|
|
909
1031
|
return r.data;
|
|
910
1032
|
}
|
|
911
1033
|
case "get_html": {
|
|
912
|
-
const r = await fetch("POST", `/browser/sessions/${sid}/get_html`, {
|
|
1034
|
+
const r = await fetch("POST", `/browser/sessions/${sid}/get_html`, {
|
|
1035
|
+
selector: action.selector,
|
|
1036
|
+
});
|
|
913
1037
|
if (!r.ok)
|
|
914
1038
|
throw new Error(browserError(r));
|
|
915
1039
|
return r.data.html;
|
|
916
1040
|
}
|
|
917
1041
|
case "query_selector_all": {
|
|
918
|
-
const r = await fetch("POST", `/browser/sessions/${sid}/query_selector_all`, {
|
|
1042
|
+
const r = await fetch("POST", `/browser/sessions/${sid}/query_selector_all`, {
|
|
1043
|
+
selector: action.selector,
|
|
1044
|
+
});
|
|
919
1045
|
if (!r.ok)
|
|
920
1046
|
throw new Error(browserError(r));
|
|
921
1047
|
return r.data;
|
|
922
1048
|
}
|
|
923
1049
|
case "screenshot": {
|
|
924
|
-
const r = await fetch("POST", `/browser/sessions/${sid}/screenshot`, {
|
|
1050
|
+
const r = await fetch("POST", `/browser/sessions/${sid}/screenshot`, {
|
|
1051
|
+
full_page: action.full_page,
|
|
1052
|
+
selector: action.selector,
|
|
1053
|
+
});
|
|
925
1054
|
if (!r.ok)
|
|
926
1055
|
throw new Error(browserError(r));
|
|
927
1056
|
// Return as data URI string so it fits in the JSON result array.
|
|
@@ -930,7 +1059,10 @@ labels, and states — everything needed to drive browser interactions.`,
|
|
|
930
1059
|
return { mime_type: d.mime_type, data: d.data };
|
|
931
1060
|
}
|
|
932
1061
|
case "wait_for_selector": {
|
|
933
|
-
const r = await fetch("POST", `/browser/sessions/${sid}/wait_for_selector`, {
|
|
1062
|
+
const r = await fetch("POST", `/browser/sessions/${sid}/wait_for_selector`, {
|
|
1063
|
+
selector: action.selector,
|
|
1064
|
+
visible: action.visible,
|
|
1065
|
+
});
|
|
934
1066
|
if (!r.ok)
|
|
935
1067
|
throw new Error(browserError(r));
|
|
936
1068
|
return { ok: true };
|
|
@@ -948,7 +1080,9 @@ labels, and states — everything needed to drive browser interactions.`,
|
|
|
948
1080
|
return { ok: true };
|
|
949
1081
|
}
|
|
950
1082
|
case "evaluate": {
|
|
951
|
-
const r = await fetch("POST", `/browser/sessions/${sid}/evaluate`, {
|
|
1083
|
+
const r = await fetch("POST", `/browser/sessions/${sid}/evaluate`, {
|
|
1084
|
+
script: action.script,
|
|
1085
|
+
});
|
|
952
1086
|
if (!r.ok)
|
|
953
1087
|
throw new Error(browserError(r));
|
|
954
1088
|
return r.data;
|
|
@@ -956,7 +1090,11 @@ labels, and states — everything needed to drive browser interactions.`,
|
|
|
956
1090
|
}
|
|
957
1091
|
}
|
|
958
1092
|
server.registerTool("browser_batch", {
|
|
959
|
-
annotations: {
|
|
1093
|
+
annotations: {
|
|
1094
|
+
title: "Run Batch of Browser Actions",
|
|
1095
|
+
readOnlyHint: false,
|
|
1096
|
+
destructiveHint: false,
|
|
1097
|
+
},
|
|
960
1098
|
description: `Execute a sequence of browser actions in a single call against an existing session.
|
|
961
1099
|
|
|
962
1100
|
Use this when you already know the full sequence of steps — it reduces round trips and
|
|
@@ -969,7 +1107,11 @@ Note: screenshots in batch results are returned as base64 strings inside the JSO
|
|
|
969
1107
|
For proper image rendering, use browser_screenshot directly.`,
|
|
970
1108
|
inputSchema: {
|
|
971
1109
|
session_id: sessionId,
|
|
972
|
-
actions: z
|
|
1110
|
+
actions: z
|
|
1111
|
+
.array(batchAction)
|
|
1112
|
+
.min(1)
|
|
1113
|
+
.max(50)
|
|
1114
|
+
.describe("Ordered list of actions to perform. Each action has a 'type' field plus type-specific parameters."),
|
|
973
1115
|
stop_on_error: z.boolean().optional().describe("Stop executing on the first failed action (default true)"),
|
|
974
1116
|
},
|
|
975
1117
|
}, async ({ session_id, actions, stop_on_error = true }) => {
|
|
@@ -986,7 +1128,12 @@ For proper image rendering, use browser_screenshot directly.`,
|
|
|
986
1128
|
results.push({ step: i, type: action.type, error: msg });
|
|
987
1129
|
if (stop_on_error) {
|
|
988
1130
|
return {
|
|
989
|
-
content: [
|
|
1131
|
+
content: [
|
|
1132
|
+
{
|
|
1133
|
+
type: "text",
|
|
1134
|
+
text: JSON.stringify({ completed: i, total: actions.length, results }),
|
|
1135
|
+
},
|
|
1136
|
+
],
|
|
990
1137
|
isError: true,
|
|
991
1138
|
};
|
|
992
1139
|
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
export declare function zrErrorCode(body: string): string | undefined;
|
|
3
|
+
export type ExtractMode = "auto" | "autoparse" | "css";
|
|
4
|
+
export type ExtractStealthOpts = {
|
|
5
|
+
css_extractor?: string;
|
|
6
|
+
js_render?: boolean;
|
|
7
|
+
premium_proxy?: boolean;
|
|
8
|
+
proxy_country?: string;
|
|
9
|
+
mode_auto?: boolean;
|
|
10
|
+
wait_for?: string;
|
|
11
|
+
wait?: number;
|
|
12
|
+
};
|
|
13
|
+
export type ExtractInput = ExtractStealthOpts & {
|
|
14
|
+
url: string;
|
|
15
|
+
mode?: ExtractMode;
|
|
16
|
+
fallback_autoparse?: boolean;
|
|
17
|
+
};
|
|
18
|
+
export type ExtractSuccess = {
|
|
19
|
+
ok: true;
|
|
20
|
+
mode: ExtractMode;
|
|
21
|
+
fellBackToAutoparse: boolean;
|
|
22
|
+
empty: boolean;
|
|
23
|
+
data: unknown;
|
|
24
|
+
html?: string;
|
|
25
|
+
raw?: string;
|
|
26
|
+
};
|
|
27
|
+
export type ExtractFailure = {
|
|
28
|
+
ok: false;
|
|
29
|
+
errorText: string;
|
|
30
|
+
};
|
|
31
|
+
export declare function buildExtractParams(apiKey: string, url: string, mode: ExtractMode, opts: ExtractStealthOpts): URLSearchParams;
|
|
32
|
+
/**
|
|
33
|
+
* Core extract logic (testable). AUTH010 on mode=auto retries once with autoparse
|
|
34
|
+
* unless fallback_autoparse is false — same behavior as the CLI extract adapter.
|
|
35
|
+
*/
|
|
36
|
+
export declare function runExtract(apiKey: string, params: ExtractInput, options?: {
|
|
37
|
+
getClientName?: () => string | undefined;
|
|
38
|
+
fetchImpl?: typeof fetch;
|
|
39
|
+
}): Promise<ExtractSuccess | ExtractFailure>;
|
|
40
|
+
export declare function registerExtractTool(server: McpServer, apiKey: string, getClientName: () => string | undefined): void;
|