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
package/README.md CHANGED
@@ -119,7 +119,7 @@ For customer AI visibility setup, use the hosted profile connection flow. Open t
119
119
 
120
120
  Browser-agent hosted sessions are direct/no-proxy by default. They do not inherit the configured SERP/Maps proxy. Use proxies for SERP/Maps tools when those tools need location or anti-bot handling, not for the interactive login browser.
121
121
 
122
- `BROWSER_AGENT_PROFILE_NAME` sets the default saved hosted browser profile for future sessions. For first-time setup, use `browser_profile_onboard`, open the returned `watch_url`, complete login in the hosted browser, then call `browser_close` to persist cookies and storage into the saved profile.
122
+ `BROWSER_AGENT_PROFILE_NAME` sets the default saved hosted browser profile for future sessions. For first-time setup, use `browser_profile_onboard`, open the returned `watch_url`, complete login, then call `browser_profile_status` until the connection is authenticated.
123
123
 
124
124
  Inside MCP clients, use `browser_profile_onboard` for customer setup and reconnects. The customer signs in through the hosted `watch_url` so they control the login session directly.
125
125
 
@@ -202,7 +202,8 @@ env = { MCP_SCRAPER_API_KEY = "sk_live_your_key" }
202
202
  ### Browser-agent tools
203
203
 
204
204
  - `browser_open` — open a direct/no-proxy hosted browser session. Pass a saved profile name to load an authenticated session. Returns a human `watch_url`.
205
- - `browser_profile_onboard` — create or load a saved hosted browser profile and open a direct/no-proxy setup browser at Google with profile saving enabled. The user clicks Sign in through the `watch_url`, then `browser_close` persists cookies and storage into the saved profile. Use this again whenever the profile disconnects.
205
+ - `browser_profile_onboard` — create or load a saved hosted browser profile auth connection and return a user takeover `watch_url`. The user signs in through that URL, then `browser_profile_status` verifies the profile is authenticated. Use this again whenever the profile disconnects.
206
+ - `browser_profile_status` — check whether a hosted browser profile auth connection is authenticated before launching it with `browser_open`.
206
207
  - `browser_screenshot` — capture a screenshot plus visible text and clickable element center coordinates and DOM bounds.
207
208
  - `browser_read` — read the current page text and elements with center coordinates and DOM bounds, without an image.
208
209
  - `browser_locate` — locate exact visible DOM elements or text ranges and return screenshot-pixel bounds.
@@ -230,7 +231,7 @@ The MCPB bundle and `mcp-scraper-combined` expose both sections through one MCP
230
231
 
231
232
  All MCP tools expose output schemas and return `structuredContent` with the IDs, URLs, CSV paths, transcripts, browser session handles, replay paths, artifacts, recipe fields, or blueprint fields needed by the next step. Browser Agent tools keep a JSON text block for older clients, but structured data is the primary contract. All tools carry MCP annotations; file-writing tools such as replay downloads and annotations state their filesystem side effects.
232
233
 
233
- The canonical tool inventory is generated at `docs/mcp-tool-manifest.generated.json`. The split `mcp-scraper` stdio server exposes 24 web-intelligence/workflow tools. The split `browser-agent` stdio server exposes 19 browser tools. The combined stdio server exposes 43 tools. The hosted MCP endpoint at `https://mcpscraper.dev/mcp` exposes the 24 web-intelligence/workflow tools plus `capture_serp_snapshot` and `capture_serp_page_snapshots` (26 total). Across all public surfaces there are 45 unique tool names.
234
+ The canonical tool inventory is generated at `docs/mcp-tool-manifest.generated.json`. The split `mcp-scraper` stdio server exposes 24 web-intelligence/workflow tools. The split `browser-agent` stdio server exposes 20 browser tools. The combined stdio server exposes 44 tools. The hosted MCP endpoint at `https://mcpscraper.dev/mcp` exposes the 24 web-intelligence/workflow tools plus `capture_serp_snapshot` and `capture_serp_page_snapshots` (26 total). Across all public surfaces there are 46 unique tool names.
234
235
 
235
236
  ## Resources
236
237
 
@@ -21921,7 +21921,7 @@ var PACKAGE_VERSION;
21921
21921
  var init_version = __esm({
21922
21922
  "src/version.ts"() {
21923
21923
  "use strict";
21924
- PACKAGE_VERSION = "0.3.8";
21924
+ PACKAGE_VERSION = "0.3.10";
21925
21925
  }
21926
21926
  });
21927
21927
 
@@ -24639,22 +24639,82 @@ async function ensureProfile(k, name) {
24639
24639
  if (!isProfileConflict(err)) throw err;
24640
24640
  }
24641
24641
  }
24642
+ function profileConnectionMatches(connection, profileName, domain) {
24643
+ if (!connection || typeof connection !== "object") return false;
24644
+ const data = connection;
24645
+ return data.profile_name === profileName && data.domain === domain;
24646
+ }
24647
+ async function findProfileConnection(k, profileName, domain) {
24648
+ for await (const connection of k.auth.connections.list({ profile_name: profileName, domain })) {
24649
+ if (profileConnectionMatches(connection, profileName, domain)) return connection;
24650
+ }
24651
+ return null;
24652
+ }
24653
+ async function findOrCreateProfileConnection(k, opts) {
24654
+ try {
24655
+ return await k.auth.connections.create({
24656
+ domain: opts.domain,
24657
+ profile_name: opts.profileName,
24658
+ ...opts.loginUrl ? { login_url: opts.loginUrl } : {}
24659
+ });
24660
+ } catch (err) {
24661
+ if (!isProfileConflict(err)) throw err;
24662
+ const existing = await findProfileConnection(k, opts.profileName, opts.domain);
24663
+ if (existing) return existing;
24664
+ throw err;
24665
+ }
24666
+ }
24667
+ function profileOnboardResult(connection, hostedUrl = null, liveViewUrl = null) {
24668
+ return {
24669
+ connectionId: String(connection.id),
24670
+ profileName: String(connection.profile_name),
24671
+ domain: String(connection.domain),
24672
+ status: String(connection.status),
24673
+ flowStatus: typeof connection.flow_status === "string" ? connection.flow_status : null,
24674
+ flowStep: typeof connection.flow_step === "string" ? connection.flow_step : null,
24675
+ flowExpiresAt: typeof connection.flow_expires_at === "string" ? connection.flow_expires_at : null,
24676
+ hostedUrl: hostedUrl ?? (typeof connection.hosted_url === "string" ? connection.hosted_url : null),
24677
+ liveViewUrl: liveViewUrl ?? (typeof connection.live_view_url === "string" ? connection.live_view_url : null),
24678
+ browserSessionId: typeof connection.browser_session_id === "string" ? connection.browser_session_id : null,
24679
+ postLoginUrl: typeof connection.post_login_url === "string" ? connection.post_login_url : null
24680
+ };
24681
+ }
24642
24682
  function explicitProxyId(value) {
24643
24683
  const trimmed = value?.trim();
24644
24684
  return trimmed || void 0;
24645
24685
  }
24686
+ async function startProfileOnboarding(opts) {
24687
+ const k = client();
24688
+ const connection = await findOrCreateProfileConnection(k, opts);
24689
+ const login = await k.auth.connections.login(connection.id);
24690
+ const state = await k.auth.connections.retrieve(connection.id);
24691
+ return profileOnboardResult(state, login.hosted_url ?? null, login.live_view_url ?? null);
24692
+ }
24693
+ async function getProfileOnboardingStatus(input) {
24694
+ const k = client();
24695
+ let connection = null;
24696
+ if (input.connectionId) {
24697
+ connection = await k.auth.connections.retrieve(input.connectionId);
24698
+ } else if (input.profileName && input.domain) {
24699
+ connection = await findProfileConnection(k, input.profileName, input.domain);
24700
+ }
24701
+ if (!connection) throw new Error("profile auth connection not found");
24702
+ return profileOnboardResult(connection);
24703
+ }
24646
24704
  async function createSession(opts = {}) {
24647
24705
  const k = client();
24648
24706
  const resolvedProxyId = explicitProxyId(opts.proxyId);
24649
24707
  const resolvedProfileName = opts.profileName ?? browserServiceProfileName();
24650
24708
  const resolvedSaveProfileChanges = opts.saveProfileChanges ?? browserServiceProfileSaveChanges();
24651
24709
  const disableDefaultProxy = opts.disableDefaultProxy ?? !resolvedProxyId;
24710
+ const startUrl = opts.startUrl?.trim();
24652
24711
  if (resolvedProfileName && resolvedSaveProfileChanges === true) {
24653
24712
  await ensureProfile(k, resolvedProfileName);
24654
24713
  }
24655
- const browser = await k.browsers.create({
24714
+ const createPayload = {
24656
24715
  stealth: opts.stealth ?? true,
24657
24716
  timeout_seconds: opts.timeoutSeconds ?? DEFAULT_TIMEOUT_SECONDS,
24717
+ ...startUrl ? { start_url: startUrl } : {},
24658
24718
  ...resolvedProxyId ? { proxy_id: resolvedProxyId } : {},
24659
24719
  ...resolvedProfileName ? {
24660
24720
  profile: {
@@ -24662,7 +24722,8 @@ async function createSession(opts = {}) {
24662
24722
  ...typeof resolvedSaveProfileChanges === "boolean" ? { save_changes: resolvedSaveProfileChanges } : {}
24663
24723
  }
24664
24724
  } : {}
24665
- });
24725
+ };
24726
+ const browser = await k.browsers.create(createPayload);
24666
24727
  const runtimeSessionId = browser.session_id;
24667
24728
  if (disableDefaultProxy) {
24668
24729
  try {
@@ -25042,6 +25103,56 @@ function buildBrowserAgentRoutes() {
25042
25103
  return next();
25043
25104
  });
25044
25105
  app2.use("*", auth);
25106
+ app2.post("/profiles/onboard", async (c) => {
25107
+ const body = await c.req.json().catch(() => ({}));
25108
+ const profileName = typeof body.profile === "string" ? body.profile.trim() : "";
25109
+ const domain = typeof body.domain === "string" ? body.domain.trim().replace(/^https?:\/\//, "").replace(/\/.*$/, "") : "chatgpt.com";
25110
+ const loginUrl = typeof body.login_url === "string" ? body.login_url.trim() : void 0;
25111
+ if (!profileName) return c.json({ error: "profile is required" }, 400);
25112
+ if (!domain) return c.json({ error: "domain is required" }, 400);
25113
+ try {
25114
+ const result = await startProfileOnboarding({ profileName, domain, loginUrl });
25115
+ return c.json({
25116
+ connection_id: result.connectionId,
25117
+ profile: result.profileName,
25118
+ domain: result.domain,
25119
+ status: result.status,
25120
+ flow_status: result.flowStatus,
25121
+ flow_step: result.flowStep,
25122
+ flow_expires_at: result.flowExpiresAt,
25123
+ hosted_url: result.hostedUrl,
25124
+ live_view_url: result.liveViewUrl,
25125
+ browser_session_id: result.browserSessionId,
25126
+ post_login_url: result.postLoginUrl
25127
+ });
25128
+ } catch (err) {
25129
+ return c.json(failure(err), 502);
25130
+ }
25131
+ });
25132
+ app2.post("/profiles/status", async (c) => {
25133
+ const body = await c.req.json().catch(() => ({}));
25134
+ const connectionId = typeof body.connection_id === "string" ? body.connection_id.trim() : void 0;
25135
+ const profileName = typeof body.profile === "string" ? body.profile.trim() : void 0;
25136
+ const domain = typeof body.domain === "string" ? body.domain.trim().replace(/^https?:\/\//, "").replace(/\/.*$/, "") : void 0;
25137
+ try {
25138
+ const result = await getProfileOnboardingStatus({ connectionId, profileName, domain });
25139
+ return c.json({
25140
+ connection_id: result.connectionId,
25141
+ profile: result.profileName,
25142
+ domain: result.domain,
25143
+ status: result.status,
25144
+ flow_status: result.flowStatus,
25145
+ flow_step: result.flowStep,
25146
+ flow_expires_at: result.flowExpiresAt,
25147
+ hosted_url: result.hostedUrl,
25148
+ live_view_url: result.liveViewUrl,
25149
+ browser_session_id: result.browserSessionId,
25150
+ post_login_url: result.postLoginUrl
25151
+ });
25152
+ } catch (err) {
25153
+ return c.json(failure(err), 404);
25154
+ }
25155
+ });
25045
25156
  app2.post("/sessions", async (c) => {
25046
25157
  const user = c.get("user");
25047
25158
  if (Number(user.balance_mc ?? 0) < BROWSER_OPEN_MIN_BALANCE_MC) {
@@ -25049,6 +25160,7 @@ function buildBrowserAgentRoutes() {
25049
25160
  }
25050
25161
  const body = await c.req.json().catch(() => ({}));
25051
25162
  const timeoutSeconds = typeof body.timeout_seconds === "number" ? body.timeout_seconds : void 0;
25163
+ const startUrl = typeof body.url === "string" ? body.url : void 0;
25052
25164
  const gate = await acquireConcurrencyGate(user, "browser_agent_session", {
25053
25165
  ttlSeconds: browserSessionLockTtlSeconds(timeoutSeconds),
25054
25166
  metadata: { label: typeof body.label === "string" ? body.label : null }
@@ -25061,6 +25173,7 @@ function buildBrowserAgentRoutes() {
25061
25173
  proxyId: typeof body.proxy_id === "string" ? body.proxy_id : void 0,
25062
25174
  profileName: typeof body.profile === "string" ? body.profile : void 0,
25063
25175
  saveProfileChanges: typeof body.save_profile_changes === "boolean" ? body.save_profile_changes : void 0,
25176
+ startUrl,
25064
25177
  disableDefaultProxy: typeof body.disable_default_proxy === "boolean" ? body.disable_default_proxy : void 0,
25065
25178
  viewport: body.viewport && typeof body.viewport === "object" ? body.viewport : void 0
25066
25179
  });