@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.
@@ -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 { content: [{ type: "text", text: msg }], isError: true };
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: { title: "Open Browser & Navigate", readOnlyHint: false, destructiveHint: false },
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.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)"),
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`, { url: params.url });
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({ session_id: session.session_id, url: nav.url, title: nav.title, expires_at: session.expires_at });
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`, { selector, text, clear_first });
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`, { selector, value });
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`, { selector, value });
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`, { direction, distance });
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`, { selector });
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`, { selector, 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`, { selector });
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: { title: "Query All Matching Elements", readOnlyHint: true, destructiveHint: false },
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`, { selector });
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.boolean().optional().describe("Capture full page including content below the fold (default false)"),
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`, { full_page, selector });
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`, { print_background, landscape, scale });
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.boolean().optional().describe("Also require the element to be visible, not just present in the DOM (default false)"),
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`, { selector, visible });
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.number().int().min(1000).max(60000).optional().describe("How long to wait in milliseconds (default 30000, max 60000)"),
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`, { cookies: mapped });
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`, { action, key, value });
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`, { tab_id });
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({ type: z.literal("type"), selector: z.string(), text: z.string(), clear_first: z.boolean().optional() }),
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({ type: z.literal("scroll"), direction: z.enum(["up", "down", "left", "right"]), distance: z.number().optional() }),
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({ type: z.literal("screenshot"), full_page: z.boolean().optional(), selector: z.string().optional() }),
783
- z.object({ type: z.literal("wait_for_selector"), selector: z.string(), visible: z.boolean().optional() }),
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`, { selector: action.selector });
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`, { selector: action.selector });
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`, { selector: action.selector, text: action.text, clear_first: action.clear_first });
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`, { selector: action.selector, value: action.value });
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`, { selector: action.selector, value: action.value });
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`, { selector: action.selector });
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`, { selector: action.selector });
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`, { selector: action.selector });
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`, { direction: action.direction, distance: action.distance });
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`, { source_selector: action.source_selector, target_selector: action.target_selector });
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`, { selector: action.selector });
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`, { selector: action.selector, attribute: action.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`, { selector: action.selector });
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`, { selector: action.selector });
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`, { full_page: action.full_page, selector: action.selector });
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`, { selector: action.selector, visible: action.visible });
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`, { script: action.script });
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: { title: "Run Batch of Browser Actions", readOnlyHint: false, destructiveHint: false },
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.array(batchAction).min(1).max(50).describe("Ordered list of actions to perform. Each action has a 'type' field plus type-specific parameters."),
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: [{ type: "text", text: JSON.stringify({ completed: i, total: actions.length, results }) }],
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;