mcp-scraper 0.3.8 → 0.3.10

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.
Files changed (36) hide show
  1. package/README.md +4 -3
  2. package/dist/bin/api-server.cjs +116 -3
  3. package/dist/bin/api-server.cjs.map +1 -1
  4. package/dist/bin/api-server.js +1 -1
  5. package/dist/bin/browser-agent-stdio-server.cjs +99 -30
  6. package/dist/bin/browser-agent-stdio-server.cjs.map +1 -1
  7. package/dist/bin/browser-agent-stdio-server.js +2 -2
  8. package/dist/bin/mcp-scraper-cli.cjs +1 -1
  9. package/dist/bin/mcp-scraper-cli.cjs.map +1 -1
  10. package/dist/bin/mcp-scraper-cli.js +1 -1
  11. package/dist/bin/mcp-scraper-combined-stdio-server.cjs +105 -36
  12. package/dist/bin/mcp-scraper-combined-stdio-server.cjs.map +1 -1
  13. package/dist/bin/mcp-scraper-combined-stdio-server.js +4 -4
  14. package/dist/bin/mcp-scraper-install.cjs +7 -7
  15. package/dist/bin/mcp-scraper-install.cjs.map +1 -1
  16. package/dist/bin/mcp-scraper-install.js +2 -2
  17. package/dist/bin/mcp-stdio-server.cjs +1 -1
  18. package/dist/bin/mcp-stdio-server.cjs.map +1 -1
  19. package/dist/bin/mcp-stdio-server.js +2 -2
  20. package/dist/{chunk-FU3WERZ7.js → chunk-CED7X4WB.js} +2 -2
  21. package/dist/{chunk-ELI6NDEG.js → chunk-KPXMPAJ3.js} +7 -7
  22. package/dist/chunk-KPXMPAJ3.js.map +1 -0
  23. package/dist/chunk-NEGW2ZEJ.js +7 -0
  24. package/dist/chunk-NEGW2ZEJ.js.map +1 -0
  25. package/dist/{chunk-VQFVA6RH.js → chunk-QPFF3V2R.js} +100 -31
  26. package/dist/chunk-QPFF3V2R.js.map +1 -0
  27. package/dist/{server-7CGBJWJY.js → server-QTV2EUKA.js} +118 -5
  28. package/dist/{server-7CGBJWJY.js.map → server-QTV2EUKA.js.map} +1 -1
  29. package/docs/mcp-tool-craft-lint.generated.md +3 -2
  30. package/docs/mcp-tool-manifest.generated.json +43 -13
  31. package/package.json +1 -1
  32. package/dist/chunk-7I6PA2UQ.js +0 -7
  33. package/dist/chunk-7I6PA2UQ.js.map +0 -1
  34. package/dist/chunk-ELI6NDEG.js.map +0 -1
  35. package/dist/chunk-VQFVA6RH.js.map +0 -1
  36. /package/dist/{chunk-FU3WERZ7.js.map → chunk-CED7X4WB.js.map} +0 -0
@@ -17,7 +17,7 @@ loadDotEnv();
17
17
  async function main() {
18
18
  const [{ serve }, { app }, { startWorker }, { migrate }] = await Promise.all([
19
19
  import("@hono/node-server"),
20
- import("../server-7CGBJWJY.js"),
20
+ import("../server-QTV2EUKA.js"),
21
21
  import("../worker-56IXWOQU.js"),
22
22
  import("../db-BE4JVB3V.js")
23
23
  ]);
@@ -27,7 +27,7 @@ function browserServiceProfileSaveChanges() {
27
27
  }
28
28
 
29
29
  // src/version.ts
30
- var PACKAGE_VERSION = "0.3.8";
30
+ var PACKAGE_VERSION = "0.3.10";
31
31
 
32
32
  // src/mcp/browser-agent-tool-schemas.ts
33
33
  var import_zod = require("zod");
@@ -53,10 +53,17 @@ var BrowserOpenInputSchema = {
53
53
  var BrowserProfileOnboardInputSchema = {
54
54
  email: import_zod.z.string().optional().describe("Optional account email used to derive a stable MCP Scraper profile name. This does not import cookies or browser state."),
55
55
  profile: import_zod.z.string().optional().describe("Optional saved MCP Scraper hosted browser profile name to create or load. If omitted, email is used to derive one."),
56
- url: import_zod.z.string().url().optional().describe("Setup URL to open after creating the direct no-proxy browser. Defaults to https://google.com/ so the user can click Sign in."),
56
+ domain: import_zod.z.string().optional().describe("Authenticated site domain to connect to the profile. Defaults to chatgpt.com for AI visibility fan-out tracking."),
57
+ login_url: import_zod.z.string().url().optional().describe("Optional login URL for the target domain. Defaults to https://chatgpt.com/ when domain is omitted."),
58
+ url: import_zod.z.string().url().optional().describe("Deprecated alias for login_url. Use login_url for the target site login page."),
57
59
  label: import_zod.z.string().optional().describe("Optional human label for this setup session."),
58
60
  timeout_seconds: import_zod.z.number().int().min(60).max(259200).optional().describe("How long the direct no-proxy setup session may live before auto-termination. Defaults to 600.")
59
61
  };
62
+ var BrowserProfileStatusInputSchema = {
63
+ connection_id: import_zod.z.string().optional().describe("Auth connection id returned by browser_profile_onboard. Prefer this when available."),
64
+ profile: import_zod.z.string().optional().describe("Saved hosted profile name to check when connection_id is unavailable."),
65
+ domain: import_zod.z.string().optional().describe("Authenticated site domain to check with profile. Defaults to chatgpt.com when omitted.")
66
+ };
60
67
  var BrowserSessionInputSchema = {
61
68
  session_id: import_zod.z.string().describe("The session id returned by browser_open or browser_list_sessions. Use only a returned session_id; do not construct one yourself.")
62
69
  };
@@ -228,15 +235,36 @@ var BrowserOpenOutputSchema = {
228
235
  var BrowserProfileOnboardOutputSchema = {
229
236
  ok: import_zod.z.boolean(),
230
237
  tool: import_zod.z.literal("browser_profile_onboard"),
231
- session_id: import_zod.z.string(),
238
+ session_id: NullableString,
239
+ auth_connection_id: import_zod.z.string(),
232
240
  watch_url: import_zod.z.string(),
233
241
  live_view_url: NullableString,
234
242
  profile: import_zod.z.string(),
243
+ domain: import_zod.z.string(),
235
244
  setup_url: import_zod.z.string(),
236
245
  account_email: NullableString,
246
+ status: import_zod.z.string(),
247
+ flow_status: NullableString,
248
+ flow_step: NullableString,
249
+ flow_expires_at: NullableString,
250
+ post_login_url: NullableString,
237
251
  next_steps: import_zod.z.array(import_zod.z.string()),
238
252
  raw: BrowserRawObject.optional()
239
253
  };
254
+ var BrowserProfileStatusOutputSchema = {
255
+ ok: import_zod.z.boolean(),
256
+ tool: import_zod.z.literal("browser_profile_status"),
257
+ session_id: NullableString,
258
+ auth_connection_id: import_zod.z.string(),
259
+ profile: import_zod.z.string(),
260
+ domain: import_zod.z.string(),
261
+ status: import_zod.z.string(),
262
+ flow_status: NullableString,
263
+ flow_step: NullableString,
264
+ flow_expires_at: NullableString,
265
+ post_login_url: NullableString,
266
+ raw: BrowserRawObject.optional()
267
+ };
240
268
  var BrowserScreenshotOutputSchema = {
241
269
  ...BrowserBaseOutput,
242
270
  tool: import_zod.z.literal("browser_screenshot"),
@@ -643,10 +671,16 @@ function replayFilePath(sessionId, replayId, filename) {
643
671
  const name = requested ? safeFilePart(requested).replace(/\.mp4$/i, "") : `${stamp}-${safeFilePart(sessionId)}-${safeFilePart(replayId)}`;
644
672
  return (0, import_node_path2.join)(outputBaseDir(), "browser-replays", `${name}.mp4`);
645
673
  }
646
- function savedProfileNameFromEmail(email) {
647
- const value = email?.trim().toLowerCase();
674
+ function slugPart(value) {
675
+ const trimmed = value?.trim().toLowerCase();
676
+ if (!trimmed) return null;
677
+ return trimmed.replace(/^https?:\/\//, "").replace(/^www\./, "").replace(/\/.*$/, "").replace(/[^a-z0-9._-]+/g, "-").replace(/[._-]+/g, "-").replace(/^-+|-+$/g, "");
678
+ }
679
+ function savedProfileNameFromEmail(email, domain) {
680
+ const value = slugPart(email);
648
681
  if (!value) return null;
649
- return value.replace(/[^a-z0-9._-]+/g, "-").replace(/[._-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80) || null;
682
+ const domainPart = slugPart(domain);
683
+ return `${value}${domainPart ? `-${domainPart}` : ""}`.slice(0, 80) || null;
650
684
  }
651
685
  function annotatedReplayFilePath(sessionId, replayId, filename) {
652
686
  const requested = filename?.trim();
@@ -737,46 +771,83 @@ function registerBrowserAgentMcpTools(server2, opts) {
737
771
  "browser_profile_onboard",
738
772
  {
739
773
  title: "Connect Hosted Browser Profile",
740
- description: "Create or load a saved MCP Scraper hosted browser profile and open a direct no-proxy setup browser with profile saving enabled. This is the customer auth setup and reconnect flow: open Google, give the user the watch_url, have them click Sign in and complete login, then call browser_close to save cookies and storage. If a saved profile later shows login controls, run this same setup flow again.",
774
+ description: "Create or load a saved MCP Scraper hosted browser profile auth connection and return a user takeover login URL. This is the customer auth setup and reconnect flow for AI visibility tracking: give the user the watch_url, let them complete login for the target domain, then call browser_profile_status until status is AUTHENTICATED. If a saved profile later shows login controls, run this same setup flow again.",
741
775
  inputSchema: BrowserProfileOnboardInputSchema,
742
776
  outputSchema: BrowserProfileOnboardOutputSchema,
743
777
  annotations: annotations("Connect Hosted Browser Profile")
744
778
  },
745
779
  async (input) => {
746
- const profile = input.profile?.trim() || savedProfileNameFromEmail(input.email) || browserServiceProfileName();
780
+ const domain = slugPart(input.domain) || "chatgpt.com";
781
+ const setupUrl = input.login_url ?? input.url ?? (domain === "chatgpt.com" ? "https://chatgpt.com/" : `https://${domain}/`);
782
+ const profile = input.profile?.trim() || savedProfileNameFromEmail(input.email, domain) || browserServiceProfileName();
747
783
  if (!profile) {
748
784
  return errorResult("browser_profile_onboard", {
749
785
  error: "profile or email is required when BROWSER_AGENT_PROFILE_NAME is not available"
750
786
  });
751
787
  }
752
- const setupUrl = input.url ?? "https://google.com/";
753
- const open = await req("POST", "/agent/sessions", {
788
+ const open = await req("POST", "/agent/profiles/onboard", {
754
789
  label: input.label ?? `Profile setup: ${profile}`,
755
790
  profile,
756
- save_profile_changes: true,
757
- disable_default_proxy: true,
758
- timeout_seconds: input.timeout_seconds
791
+ domain,
792
+ login_url: setupUrl
759
793
  });
760
794
  if (!open.ok) return errorResult("browser_profile_onboard", open.data);
761
- const session = open.data;
762
- const goto = await req("POST", `/agent/sessions/${session.session_id}/goto`, { url: setupUrl });
763
- if (!goto.ok) return errorResult("browser_profile_onboard", goto.data, session.session_id);
764
795
  return structuredResult({
765
796
  ok: true,
766
797
  tool: "browser_profile_onboard",
767
- session_id: session.session_id,
768
- watch_url: `${consoleBase}/console/${session.session_id}`,
769
- live_view_url: session.live_view_url ?? null,
798
+ session_id: null,
799
+ auth_connection_id: open.data.connection_id,
800
+ watch_url: open.data.hosted_url,
801
+ live_view_url: open.data.live_view_url ?? null,
770
802
  profile,
803
+ domain,
771
804
  setup_url: setupUrl,
772
805
  account_email: input.email?.trim() || null,
806
+ status: open.data.status ?? null,
807
+ flow_status: open.data.flow_status ?? null,
808
+ flow_step: open.data.flow_step ?? null,
809
+ flow_expires_at: open.data.flow_expires_at ?? null,
810
+ post_login_url: open.data.post_login_url ?? null,
773
811
  next_steps: [
774
- "Open the watch_url and let the user click Sign in on Google.",
775
- "After the user confirms login is complete, call browser_close with this session_id to persist cookies and storage to the saved profile.",
776
- "If the saved profile later shows login controls or disconnects, repeat browser_profile_onboard and close the setup session again.",
777
- "Use BROWSER_AGENT_PROFILE_NAME or browser_open.profile with this profile name for future authenticated sessions."
812
+ "Open the watch_url and let the user complete login for the target domain.",
813
+ "Call browser_profile_status with auth_connection_id until status is AUTHENTICATED.",
814
+ "After authentication, call browser_open with this profile name and the target site URL.",
815
+ "If the saved profile later shows login controls or disconnects, repeat browser_profile_onboard."
778
816
  ],
779
- raw: session
817
+ raw: open.data
818
+ });
819
+ }
820
+ );
821
+ server2.registerTool(
822
+ "browser_profile_status",
823
+ {
824
+ title: "Check Browser Profile Auth Status",
825
+ description: "Check whether a hosted browser profile auth connection is authenticated after browser_profile_onboard. Use auth_connection_id when available. Once status is AUTHENTICATED, use browser_open with the returned profile.",
826
+ inputSchema: BrowserProfileStatusInputSchema,
827
+ outputSchema: BrowserProfileStatusOutputSchema,
828
+ annotations: annotations("Check Browser Profile Auth Status", true)
829
+ },
830
+ async (input) => {
831
+ const domain = slugPart(input.domain) || "chatgpt.com";
832
+ const res = await req("POST", "/agent/profiles/status", {
833
+ ...input.connection_id ? { connection_id: input.connection_id } : {},
834
+ ...input.profile ? { profile: input.profile } : {},
835
+ domain
836
+ });
837
+ if (!res.ok) return errorResult("browser_profile_status", res.data);
838
+ return structuredResult({
839
+ ok: true,
840
+ tool: "browser_profile_status",
841
+ session_id: null,
842
+ auth_connection_id: res.data.connection_id,
843
+ profile: res.data.profile,
844
+ domain: res.data.domain,
845
+ status: res.data.status,
846
+ flow_status: res.data.flow_status ?? null,
847
+ flow_step: res.data.flow_step ?? null,
848
+ flow_expires_at: res.data.flow_expires_at ?? null,
849
+ post_login_url: res.data.post_login_url ?? null,
850
+ raw: res.data
780
851
  });
781
852
  }
782
853
  );
@@ -784,7 +855,7 @@ function registerBrowserAgentMcpTools(server2, opts) {
784
855
  "browser_open",
785
856
  {
786
857
  title: "Open Browser Session",
787
- description: "Open a direct no-proxy hosted browser session you can drive. Pass a saved profile name to load an authenticated session for a site. For customer login setup or reconnects, use browser_profile_onboard first so the user signs in through a watch_url, then call browser_close to save the profile. Returns a session_id used by all other browser_* tools.",
858
+ description: "Open a direct no-proxy hosted browser session you can drive. Pass a saved profile name to load an authenticated session for a site. For customer login setup or reconnects, use browser_profile_onboard first, then call browser_profile_status until status is AUTHENTICATED. Returns a session_id used by all other browser_* tools.",
788
859
  inputSchema: BrowserOpenInputSchema,
789
860
  outputSchema: BrowserOpenOutputSchema,
790
861
  annotations: annotations("Open Browser Session")
@@ -797,13 +868,11 @@ function registerBrowserAgentMcpTools(server2, opts) {
797
868
  ...profile ? { profile } : {},
798
869
  ...profile && typeof saveProfileChanges === "boolean" ? { save_profile_changes: saveProfileChanges } : {},
799
870
  disable_default_proxy: true,
800
- timeout_seconds: input.timeout_seconds
871
+ timeout_seconds: input.timeout_seconds,
872
+ ...input.url ? { url: input.url } : {}
801
873
  });
802
874
  if (!open.ok) return errorResult("browser_open", open.data);
803
875
  const session = open.data;
804
- if (input.url) {
805
- await req("POST", `/agent/sessions/${session.session_id}/goto`, { url: input.url });
806
- }
807
876
  return structuredResult({
808
877
  ok: true,
809
878
  tool: "browser_open",
@@ -1208,7 +1277,7 @@ function registerBrowserAgentMcpTools(server2, opts) {
1208
1277
  "browser_capture_fanout",
1209
1278
  {
1210
1279
  title: "Capture AI Search Fan-Out",
1211
- description: `Capture the query fan-out behind a ChatGPT or Claude web-search answer for Answer Engine Optimization (AEO): the exact sub-queries the model issued, every researched URL split into cited vs browsed-only (with citation frequency and snippet), each source tagged by category (First-party/vendor, News/media, Reddit, Social/video, Encyclopedia, Review site, Docs, Blog), plus top sourced sites and citation order. This returns the raw structured data; YOU analyze it \u2014 classify each sub-query by funnel stage (Problem-aware, Solution-aware, Decision-aware, Retention) and type (BoFu, Branded, Comparison, How-to, Search operator), name the brands the model researched, and give AEO insights. Sample asks: "capture the fan-out for this ChatGPT answer", "what did Claude search and cite for best CRM", "show the sub-queries and sources behind this AI answer". WRITE NOTE: passing prompt submits a real message in the user's logged-in account (a new conversation turn) \u2014 only send when the user wants that; omit prompt to capture a prompt the user just ran. Setup, if not already connected or if the profile disconnected: call browser_profile_onboard, give the user the watch_url, let them click Sign in on Google, then call browser_close after they confirm login. After that, browser_open the saved direct no-proxy profile and go to chatgpt.com or claude.ai. Fan-out is captured only as it streams, so the session must be open when the prompt runs. NOT for Google AI Overview citations \u2014 use harvest_paa for those; this tool is ChatGPT and Claude only.`,
1280
+ description: `Capture the query fan-out behind a ChatGPT or Claude web-search answer for Answer Engine Optimization (AEO): the exact sub-queries the model issued, every researched URL split into cited vs browsed-only (with citation frequency and snippet), each source tagged by category (First-party/vendor, News/media, Reddit, Social/video, Encyclopedia, Review site, Docs, Blog), plus top sourced sites and citation order. This returns the raw structured data; YOU analyze it \u2014 classify each sub-query by funnel stage (Problem-aware, Solution-aware, Decision-aware, Retention) and type (BoFu, Branded, Comparison, How-to, Search operator), name the brands the model researched, and give AEO insights. Sample asks: "capture the fan-out for this ChatGPT answer", "what did Claude search and cite for best CRM", "show the sub-queries and sources behind this AI answer". WRITE NOTE: passing prompt submits a real message in the user's logged-in account (a new conversation turn) \u2014 only send when the user wants that; omit prompt to capture a prompt the user just ran. Setup, if not already connected or if the profile disconnected: call browser_profile_onboard, give the user the watch_url, let them complete login, then call browser_profile_status until status is AUTHENTICATED. After that, browser_open the saved direct no-proxy profile and go to chatgpt.com or claude.ai. Fan-out is captured only as it streams, so the session must be open when the prompt runs. NOT for Google AI Overview citations \u2014 use harvest_paa for those; this tool is ChatGPT and Claude only.`,
1212
1281
  inputSchema: BrowserCaptureFanoutInputSchema,
1213
1282
  outputSchema: BrowserCaptureFanoutOutputSchema,
1214
1283
  annotations: annotations("Capture AI Search Fan-Out")