mcp-scraper 0.3.9 → 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 +109 -1
  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 +97 -25
  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 +103 -31
  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-KDKQ5Y7V.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-BRUVQRJK.js → chunk-QPFF3V2R.js} +98 -26
  26. package/dist/chunk-QPFF3V2R.js.map +1 -0
  27. package/dist/{server-ZSHE4R5Z.js → server-QTV2EUKA.js} +111 -3
  28. package/dist/{server-ZSHE4R5Z.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-7VNTN4Q7.js +0 -7
  33. package/dist/chunk-7VNTN4Q7.js.map +0 -1
  34. package/dist/chunk-BRUVQRJK.js.map +0 -1
  35. package/dist/chunk-ELI6NDEG.js.map +0 -1
  36. /package/dist/{chunk-KDKQ5Y7V.js.map → chunk-CED7X4WB.js.map} +0 -0
@@ -2,10 +2,10 @@
2
2
  import {
3
3
  HttpMcpToolExecutor,
4
4
  buildPaaExtractorMcpServer
5
- } from "../chunk-KDKQ5Y7V.js";
5
+ } from "../chunk-CED7X4WB.js";
6
6
  import "../chunk-M2S27J6Z.js";
7
7
  import "../chunk-LFATOGDF.js";
8
- import "../chunk-7VNTN4Q7.js";
8
+ import "../chunk-NEGW2ZEJ.js";
9
9
 
10
10
  // bin/mcp-stdio-server.ts
11
11
  import { readFileSync } from "fs";
@@ -7,7 +7,7 @@ import {
7
7
  } from "./chunk-LFATOGDF.js";
8
8
  import {
9
9
  PACKAGE_VERSION
10
- } from "./chunk-7VNTN4Q7.js";
10
+ } from "./chunk-NEGW2ZEJ.js";
11
11
 
12
12
  // src/harvest-timeout.ts
13
13
  var VERCEL_FUNCTION_MAX_MS = 3e5;
@@ -3335,4 +3335,4 @@ export {
3335
3335
  registerPaaExtractorMcpTools,
3336
3336
  HttpMcpToolExecutor
3337
3337
  };
3338
- //# sourceMappingURL=chunk-KDKQ5Y7V.js.map
3338
+ //# sourceMappingURL=chunk-CED7X4WB.js.map
@@ -48,14 +48,14 @@ function renderInstallTerminal(options) {
48
48
  colorize(ascii, "amber", color),
49
49
  `${colorize("MCP Scraper Agent", "cyan", color)} . v${options.version} . mcpscraper.dev`,
50
50
  "1/1 install surfaces ready",
51
- colorize("Newest: saved hosted browser profiles for AI visibility. Open Google, share the watch_url, user signs in, close to save. Browser sessions are direct/no-proxy by default.", "lime", color),
51
+ colorize("Newest: saved hosted browser profiles for AI visibility. Share the watch_url, user signs in, then verify with browser_profile_status. Browser sessions are direct/no-proxy by default.", "lime", color),
52
52
  "",
53
- `${colorize("Tools", "cyan", color)} ${colorize("(43 MCP tools)", "muted", color)}`,
53
+ `${colorize("Tools", "cyan", color)} ${colorize("(44 MCP tools)", "muted", color)}`,
54
54
  toolRow("search", ["harvest_paa", "search_serp", "maps_search", "maps_place_intel"], color),
55
55
  toolRow("extract", ["extract_url", "map_site_urls", "extract_site", "directory_workflow"], color),
56
56
  toolRow("build", ["rank_tracker_blueprint", "cron plan", "database prompt"], color),
57
57
  toolRow("media", ["youtube_harvest", "youtube_transcribe", "facebook_ad_search", "facebook_page_intel", "facebook_ad_transcribe", "facebook_video_transcribe", "instagram_profile_content", "instagram_media_download"], color),
58
- toolRow("browser", ["browser_open", "browser_profile_onboard", "browser_close", "browser_screenshot", "browser_read", "browser_locate", "browser_replay_mark", "browser_replay_annotate"], color),
58
+ toolRow("browser", ["browser_open", "browser_profile_onboard", "browser_profile_status", "browser_close", "browser_screenshot", "browser_read", "browser_locate", "browser_replay_mark", "browser_replay_annotate"], color),
59
59
  toolRow("account", ["credits_info", "reports", "MCP resources"], color),
60
60
  `${colorize("Workflows", "cyan", color)} ${colorize("(MCP + CLI + API)", "muted", color)}`,
61
61
  toolRow("route", ["workflow_list", "workflow_suggest", "workflow_run", "workflow_step", "workflow_status", "workflow_artifact_read"], color),
@@ -65,7 +65,7 @@ function renderInstallTerminal(options) {
65
65
  "Run mcp-scraper-install for this visible card. Run mcp-scraper-cli for setup utilities and subcommands.",
66
66
  "Run mcp-scraper-combined in a human terminal to print this card; MCP clients get the same command as a silent stdio server.",
67
67
  "Explicit card command: npx -y -p mcp-scraper@latest mcp-scraper-install",
68
- "Customer auth setup: run browser_profile_onboard at https://google.com/, send the watch_url, let the user click Sign in, then browser_close to save.",
68
+ "Customer auth setup: run browser_profile_onboard, send the watch_url, let the user sign in, then call browser_profile_status until AUTHENTICATED.",
69
69
  "Reconnect setup: if a saved profile shows login controls later, run browser_profile_onboard again with the same profile name.",
70
70
  "Start with workflow_suggest for broad jobs like market analysis, ICP research, CRO audits, brand briefs, content gaps, and AI visibility.",
71
71
  "For MCP clients, use mcp-scraper-combined so one install can mix SERP, Maps, browser, reports, and saved MCP resources.",
@@ -77,8 +77,8 @@ function renderInstallTerminal(options) {
77
77
  "npx -y -p mcp-scraper@latest mcp-scraper-cli doctor",
78
78
  "",
79
79
  colorize("Hosted profile setup", "amber", color),
80
- 'In your MCP client, call browser_profile_onboard with profile="seo-example-com" and url="https://google.com/".',
81
- "Give the returned watch_url to the user. After they sign in, call browser_close to persist the saved hosted profile.",
80
+ 'In your MCP client, call browser_profile_onboard with email="seo@example.com" and domain="chatgpt.com".',
81
+ "Give the returned watch_url to the user. After they sign in, call browser_profile_status, then browser_open with the returned profile.",
82
82
  "",
83
83
  colorize("Claude Code one-command setup", "amber", color),
84
84
  claudeCommand,
@@ -100,4 +100,4 @@ function renderInstallTerminal(options) {
100
100
  export {
101
101
  renderInstallTerminal
102
102
  };
103
- //# sourceMappingURL=chunk-ELI6NDEG.js.map
103
+ //# sourceMappingURL=chunk-KPXMPAJ3.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/install-terminal.ts"],"sourcesContent":["export interface InstallTerminalOptions {\n color?: boolean\n version: string\n apiKeyConfigured?: boolean\n}\n\ntype Tone = 'cyan' | 'lime' | 'amber' | 'red' | 'muted' | 'bold'\n\nconst CODES: Record<Tone | 'reset', string> = {\n reset: '\\x1b[0m',\n cyan: '\\x1b[36m',\n lime: '\\x1b[32m',\n amber: '\\x1b[33m',\n red: '\\x1b[31m',\n muted: '\\x1b[90m',\n bold: '\\x1b[1m',\n}\n\nfunction colorize(value: string, tone: Tone, enabled: boolean): string {\n if (!enabled) return value\n return `${CODES[tone]}${value}${CODES.reset}`\n}\n\nfunction toolRow(label: string, tools: string[], enabled: boolean): string {\n const padded = label.padEnd(9, ' ')\n return ` ${colorize(padded, 'muted', enabled)} ${tools.join(colorize(' . ', 'muted', enabled))}`\n}\n\nexport function renderInstallTerminal(options: InstallTerminalOptions): string {\n const color = options.color ?? true\n const apiKeyValue = options.apiKeyConfigured ? '$MCP_SCRAPER_API_KEY' : 'sk_live_your_key'\n const ascii = String.raw`\n __ __ ____ ____ \n| \\/ |/ ___| _ \\\n| |\\/| | | | |_) |\n| | | | |___| __/\n|_| |_|\\____|_|\n\n ____ ____ ____ _ ____ _____ ____\n/ ___| / ___| _ \\ / \\ | _ \\| ____| _ \\\n\\___ \\| | | |_) | / _ \\ | |_) | _| | |_) |\n ___) | |___| _ < / ___ \\| __/| |___| _ <\n|____/ \\____|_| \\_\\/_/ \\_\\_| |_____|_| \\_\\\n`\n\n const claudeCommand = [\n `MCP_SCRAPER_API_KEY=${apiKeyValue} npx -y -p mcp-scraper@latest \\\\`,\n ' mcp-scraper-cli agent install claude --apply',\n ].join('\\n')\n\n const codexConfig = [\n '[mcp_servers.mcp-scraper]',\n 'command = \"npx\"',\n 'args = [\"-y\", \"-p\", \"mcp-scraper@latest\", \"mcp-scraper-combined\"]',\n `env = { MCP_SCRAPER_API_KEY = \"${apiKeyValue}\" }`,\n ].join('\\n')\n\n return [\n colorize(`mcp-scraper v${options.version}`, 'bold', color),\n colorize('> mcp-scraper-install', 'muted', color),\n colorize(ascii, 'amber', color),\n `${colorize('MCP Scraper Agent', 'cyan', color)} . v${options.version} . mcpscraper.dev`,\n '1/1 install surfaces ready',\n colorize('Newest: saved hosted browser profiles for AI visibility. Share the watch_url, user signs in, then verify with browser_profile_status. Browser sessions are direct/no-proxy by default.', 'lime', color),\n '',\n `${colorize('Tools', 'cyan', color)} ${colorize('(44 MCP tools)', 'muted', color)}`,\n toolRow('search', ['harvest_paa', 'search_serp', 'maps_search', 'maps_place_intel'], color),\n toolRow('extract', ['extract_url', 'map_site_urls', 'extract_site', 'directory_workflow'], color),\n toolRow('build', ['rank_tracker_blueprint', 'cron plan', 'database prompt'], color),\n toolRow('media', ['youtube_harvest', 'youtube_transcribe', 'facebook_ad_search', 'facebook_page_intel', 'facebook_ad_transcribe', 'facebook_video_transcribe', 'instagram_profile_content', 'instagram_media_download'], color),\n toolRow('browser', ['browser_open', 'browser_profile_onboard', 'browser_profile_status', 'browser_close', 'browser_screenshot', 'browser_read', 'browser_locate', 'browser_replay_mark', 'browser_replay_annotate'], color),\n toolRow('account', ['credits_info', 'reports', 'MCP resources'], color),\n `${colorize('Workflows', 'cyan', color)} ${colorize('(MCP + CLI + API)', 'muted', color)}`,\n toolRow('route', ['workflow_list', 'workflow_suggest', 'workflow_run', 'workflow_step', 'workflow_status', 'workflow_artifact_read'], color),\n toolRow('seo', ['directory', 'agent-packet', 'competitive audit', 'map/serp comparison', 'PAA/AIO briefs', 'scheduled runs'], color),\n '',\n colorize('Usage tips:', 'amber', color),\n 'Run mcp-scraper-install for this visible card. Run mcp-scraper-cli for setup utilities and subcommands.',\n 'Run mcp-scraper-combined in a human terminal to print this card; MCP clients get the same command as a silent stdio server.',\n 'Explicit card command: npx -y -p mcp-scraper@latest mcp-scraper-install',\n 'Customer auth setup: run browser_profile_onboard, send the watch_url, let the user sign in, then call browser_profile_status until AUTHENTICATED.',\n 'Reconnect setup: if a saved profile shows login controls later, run browser_profile_onboard again with the same profile name.',\n 'Start with workflow_suggest for broad jobs like market analysis, ICP research, CRO audits, brand briefs, content gaps, and AI visibility.',\n 'For MCP clients, use mcp-scraper-combined so one install can mix SERP, Maps, browser, reports, and saved MCP resources.',\n 'If you hit the concurrency limit, add an extra slot for $5/month with mcp-scraper-cli billing concurrency checkout.',\n '',\n `${colorize('Ready.', 'lime', color)} Install the combined MCP server with one command:`,\n '',\n colorize('Setup doctor', 'amber', color),\n 'npx -y -p mcp-scraper@latest mcp-scraper-cli doctor',\n '',\n colorize('Hosted profile setup', 'amber', color),\n 'In your MCP client, call browser_profile_onboard with email=\"seo@example.com\" and domain=\"chatgpt.com\".',\n 'Give the returned watch_url to the user. After they sign in, call browser_profile_status, then browser_open with the returned profile.',\n '',\n colorize('Claude Code one-command setup', 'amber', color),\n claudeCommand,\n 'Then fully exit Claude Code and open a new Claude terminal. Check with: claude mcp list',\n '',\n colorize('Codex config', 'amber', color),\n codexConfig,\n '',\n colorize('Claude Desktop Extension', 'amber', color),\n 'Download: https://mcpscraper.dev/downloads/mcp-scraper.mcpb',\n '',\n colorize('Safety note:', 'muted', color),\n 'mcp-scraper-combined prints this card only when stdin/stdout are an interactive TTY. In MCP clients it writes only JSON-RPC to stdout.',\n 'Use --stdio or MCP_SCRAPER_FORCE_STDIO=1 to force server mode from a terminal.',\n '',\n ].join('\\n')\n}\n"],"mappings":";AAQA,IAAM,QAAwC;AAAA,EAC5C,OAAO;AAAA,EACP,MAAM;AAAA,EACN,MAAM;AAAA,EACN,OAAO;AAAA,EACP,KAAK;AAAA,EACL,OAAO;AAAA,EACP,MAAM;AACR;AAEA,SAAS,SAAS,OAAe,MAAY,SAA0B;AACrE,MAAI,CAAC,QAAS,QAAO;AACrB,SAAO,GAAG,MAAM,IAAI,CAAC,GAAG,KAAK,GAAG,MAAM,KAAK;AAC7C;AAEA,SAAS,QAAQ,OAAe,OAAiB,SAA0B;AACzE,QAAM,SAAS,MAAM,OAAO,GAAG,GAAG;AAClC,SAAO,KAAK,SAAS,QAAQ,SAAS,OAAO,CAAC,IAAI,MAAM,KAAK,SAAS,SAAS,SAAS,OAAO,CAAC,CAAC;AACnG;AAEO,SAAS,sBAAsB,SAAyC;AAC7E,QAAM,QAAQ,QAAQ,SAAS;AAC/B,QAAM,cAAc,QAAQ,mBAAmB,yBAAyB;AACxE,QAAM,QAAQ,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAcrB,QAAM,gBAAgB;AAAA,IACpB,uBAAuB,WAAW;AAAA,IAClC;AAAA,EACF,EAAE,KAAK,IAAI;AAEX,QAAM,cAAc;AAAA,IAClB;AAAA,IACA;AAAA,IACA;AAAA,IACA,kCAAkC,WAAW;AAAA,EAC/C,EAAE,KAAK,IAAI;AAEX,SAAO;AAAA,IACL,SAAS,gBAAgB,QAAQ,OAAO,IAAI,QAAQ,KAAK;AAAA,IACzD,SAAS,yBAAyB,SAAS,KAAK;AAAA,IAChD,SAAS,OAAO,SAAS,KAAK;AAAA,IAC9B,GAAG,SAAS,qBAAqB,QAAQ,KAAK,CAAC,SAAS,QAAQ,OAAO;AAAA,IACvE;AAAA,IACA,SAAS,0LAA0L,QAAQ,KAAK;AAAA,IAChN;AAAA,IACA,GAAG,SAAS,SAAS,QAAQ,KAAK,CAAC,KAAK,SAAS,kBAAkB,SAAS,KAAK,CAAC;AAAA,IAClF,QAAQ,UAAU,CAAC,eAAe,eAAe,eAAe,kBAAkB,GAAG,KAAK;AAAA,IAC1F,QAAQ,WAAW,CAAC,eAAe,iBAAiB,gBAAgB,oBAAoB,GAAG,KAAK;AAAA,IAChG,QAAQ,SAAS,CAAC,0BAA0B,aAAa,iBAAiB,GAAG,KAAK;AAAA,IAClF,QAAQ,SAAS,CAAC,mBAAmB,sBAAsB,sBAAsB,uBAAuB,0BAA0B,6BAA6B,6BAA6B,0BAA0B,GAAG,KAAK;AAAA,IAC9N,QAAQ,WAAW,CAAC,gBAAgB,2BAA2B,0BAA0B,iBAAiB,sBAAsB,gBAAgB,kBAAkB,uBAAuB,yBAAyB,GAAG,KAAK;AAAA,IAC1N,QAAQ,WAAW,CAAC,gBAAgB,WAAW,eAAe,GAAG,KAAK;AAAA,IACtE,GAAG,SAAS,aAAa,QAAQ,KAAK,CAAC,KAAK,SAAS,qBAAqB,SAAS,KAAK,CAAC;AAAA,IACzF,QAAQ,SAAS,CAAC,iBAAiB,oBAAoB,gBAAgB,iBAAiB,mBAAmB,wBAAwB,GAAG,KAAK;AAAA,IAC3I,QAAQ,OAAO,CAAC,aAAa,gBAAgB,qBAAqB,uBAAuB,kBAAkB,gBAAgB,GAAG,KAAK;AAAA,IACnI;AAAA,IACA,SAAS,eAAe,SAAS,KAAK;AAAA,IACtC;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAG,SAAS,UAAU,QAAQ,KAAK,CAAC;AAAA,IACpC;AAAA,IACA,SAAS,gBAAgB,SAAS,KAAK;AAAA,IACvC;AAAA,IACA;AAAA,IACA,SAAS,wBAAwB,SAAS,KAAK;AAAA,IAC/C;AAAA,IACA;AAAA,IACA;AAAA,IACA,SAAS,iCAAiC,SAAS,KAAK;AAAA,IACxD;AAAA,IACA;AAAA,IACA;AAAA,IACA,SAAS,gBAAgB,SAAS,KAAK;AAAA,IACvC;AAAA,IACA;AAAA,IACA,SAAS,4BAA4B,SAAS,KAAK;AAAA,IACnD;AAAA,IACA;AAAA,IACA,SAAS,gBAAgB,SAAS,KAAK;AAAA,IACvC;AAAA,IACA;AAAA,IACA;AAAA,EACF,EAAE,KAAK,IAAI;AACb;","names":[]}
@@ -0,0 +1,7 @@
1
+ // src/version.ts
2
+ var PACKAGE_VERSION = "0.3.10";
3
+
4
+ export {
5
+ PACKAGE_VERSION
6
+ };
7
+ //# sourceMappingURL=chunk-NEGW2ZEJ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/version.ts"],"sourcesContent":["export const PACKAGE_VERSION = '0.3.10'\n"],"mappings":";AAAO,IAAM,kBAAkB;","names":[]}
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-WN7PBKMV.js";
5
5
  import {
6
6
  PACKAGE_VERSION
7
- } from "./chunk-7VNTN4Q7.js";
7
+ } from "./chunk-NEGW2ZEJ.js";
8
8
 
9
9
  // src/mcp/browser-agent-mcp-server.ts
10
10
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
@@ -36,10 +36,17 @@ var BrowserOpenInputSchema = {
36
36
  var BrowserProfileOnboardInputSchema = {
37
37
  email: z.string().optional().describe("Optional account email used to derive a stable MCP Scraper profile name. This does not import cookies or browser state."),
38
38
  profile: z.string().optional().describe("Optional saved MCP Scraper hosted browser profile name to create or load. If omitted, email is used to derive one."),
39
- url: 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."),
39
+ domain: z.string().optional().describe("Authenticated site domain to connect to the profile. Defaults to chatgpt.com for AI visibility fan-out tracking."),
40
+ login_url: z.string().url().optional().describe("Optional login URL for the target domain. Defaults to https://chatgpt.com/ when domain is omitted."),
41
+ url: z.string().url().optional().describe("Deprecated alias for login_url. Use login_url for the target site login page."),
40
42
  label: z.string().optional().describe("Optional human label for this setup session."),
41
43
  timeout_seconds: 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.")
42
44
  };
45
+ var BrowserProfileStatusInputSchema = {
46
+ connection_id: z.string().optional().describe("Auth connection id returned by browser_profile_onboard. Prefer this when available."),
47
+ profile: z.string().optional().describe("Saved hosted profile name to check when connection_id is unavailable."),
48
+ domain: z.string().optional().describe("Authenticated site domain to check with profile. Defaults to chatgpt.com when omitted.")
49
+ };
43
50
  var BrowserSessionInputSchema = {
44
51
  session_id: 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.")
45
52
  };
@@ -211,15 +218,36 @@ var BrowserOpenOutputSchema = {
211
218
  var BrowserProfileOnboardOutputSchema = {
212
219
  ok: z.boolean(),
213
220
  tool: z.literal("browser_profile_onboard"),
214
- session_id: z.string(),
221
+ session_id: NullableString,
222
+ auth_connection_id: z.string(),
215
223
  watch_url: z.string(),
216
224
  live_view_url: NullableString,
217
225
  profile: z.string(),
226
+ domain: z.string(),
218
227
  setup_url: z.string(),
219
228
  account_email: NullableString,
229
+ status: z.string(),
230
+ flow_status: NullableString,
231
+ flow_step: NullableString,
232
+ flow_expires_at: NullableString,
233
+ post_login_url: NullableString,
220
234
  next_steps: z.array(z.string()),
221
235
  raw: BrowserRawObject.optional()
222
236
  };
237
+ var BrowserProfileStatusOutputSchema = {
238
+ ok: z.boolean(),
239
+ tool: z.literal("browser_profile_status"),
240
+ session_id: NullableString,
241
+ auth_connection_id: z.string(),
242
+ profile: z.string(),
243
+ domain: z.string(),
244
+ status: z.string(),
245
+ flow_status: NullableString,
246
+ flow_step: NullableString,
247
+ flow_expires_at: NullableString,
248
+ post_login_url: NullableString,
249
+ raw: BrowserRawObject.optional()
250
+ };
223
251
  var BrowserScreenshotOutputSchema = {
224
252
  ...BrowserBaseOutput,
225
253
  tool: z.literal("browser_screenshot"),
@@ -626,10 +654,16 @@ function replayFilePath(sessionId, replayId, filename) {
626
654
  const name = requested ? safeFilePart(requested).replace(/\.mp4$/i, "") : `${stamp}-${safeFilePart(sessionId)}-${safeFilePart(replayId)}`;
627
655
  return join2(outputBaseDir(), "browser-replays", `${name}.mp4`);
628
656
  }
629
- function savedProfileNameFromEmail(email) {
630
- const value = email?.trim().toLowerCase();
657
+ function slugPart(value) {
658
+ const trimmed = value?.trim().toLowerCase();
659
+ if (!trimmed) return null;
660
+ return trimmed.replace(/^https?:\/\//, "").replace(/^www\./, "").replace(/\/.*$/, "").replace(/[^a-z0-9._-]+/g, "-").replace(/[._-]+/g, "-").replace(/^-+|-+$/g, "");
661
+ }
662
+ function savedProfileNameFromEmail(email, domain) {
663
+ const value = slugPart(email);
631
664
  if (!value) return null;
632
- return value.replace(/[^a-z0-9._-]+/g, "-").replace(/[._-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80) || null;
665
+ const domainPart = slugPart(domain);
666
+ return `${value}${domainPart ? `-${domainPart}` : ""}`.slice(0, 80) || null;
633
667
  }
634
668
  function annotatedReplayFilePath(sessionId, replayId, filename) {
635
669
  const requested = filename?.trim();
@@ -720,45 +754,83 @@ function registerBrowserAgentMcpTools(server, opts) {
720
754
  "browser_profile_onboard",
721
755
  {
722
756
  title: "Connect Hosted Browser Profile",
723
- 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.",
757
+ 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.",
724
758
  inputSchema: BrowserProfileOnboardInputSchema,
725
759
  outputSchema: BrowserProfileOnboardOutputSchema,
726
760
  annotations: annotations("Connect Hosted Browser Profile")
727
761
  },
728
762
  async (input) => {
729
- const profile = input.profile?.trim() || savedProfileNameFromEmail(input.email) || browserServiceProfileName();
763
+ const domain = slugPart(input.domain) || "chatgpt.com";
764
+ const setupUrl = input.login_url ?? input.url ?? (domain === "chatgpt.com" ? "https://chatgpt.com/" : `https://${domain}/`);
765
+ const profile = input.profile?.trim() || savedProfileNameFromEmail(input.email, domain) || browserServiceProfileName();
730
766
  if (!profile) {
731
767
  return errorResult("browser_profile_onboard", {
732
768
  error: "profile or email is required when BROWSER_AGENT_PROFILE_NAME is not available"
733
769
  });
734
770
  }
735
- const setupUrl = input.url ?? "https://google.com/";
736
- const open = await req("POST", "/agent/sessions", {
771
+ const open = await req("POST", "/agent/profiles/onboard", {
737
772
  label: input.label ?? `Profile setup: ${profile}`,
738
773
  profile,
739
- save_profile_changes: true,
740
- disable_default_proxy: true,
741
- timeout_seconds: input.timeout_seconds,
742
- url: setupUrl
774
+ domain,
775
+ login_url: setupUrl
743
776
  });
744
777
  if (!open.ok) return errorResult("browser_profile_onboard", open.data);
745
- const session = open.data;
746
778
  return structuredResult({
747
779
  ok: true,
748
780
  tool: "browser_profile_onboard",
749
- session_id: session.session_id,
750
- watch_url: `${consoleBase}/console/${session.session_id}`,
751
- live_view_url: session.live_view_url ?? null,
781
+ session_id: null,
782
+ auth_connection_id: open.data.connection_id,
783
+ watch_url: open.data.hosted_url,
784
+ live_view_url: open.data.live_view_url ?? null,
752
785
  profile,
786
+ domain,
753
787
  setup_url: setupUrl,
754
788
  account_email: input.email?.trim() || null,
789
+ status: open.data.status ?? null,
790
+ flow_status: open.data.flow_status ?? null,
791
+ flow_step: open.data.flow_step ?? null,
792
+ flow_expires_at: open.data.flow_expires_at ?? null,
793
+ post_login_url: open.data.post_login_url ?? null,
755
794
  next_steps: [
756
- "Open the watch_url and let the user click Sign in on Google.",
757
- "After the user confirms login is complete, call browser_close with this session_id to persist cookies and storage to the saved profile.",
758
- "If the saved profile later shows login controls or disconnects, repeat browser_profile_onboard and close the setup session again.",
759
- "Use BROWSER_AGENT_PROFILE_NAME or browser_open.profile with this profile name for future authenticated sessions."
795
+ "Open the watch_url and let the user complete login for the target domain.",
796
+ "Call browser_profile_status with auth_connection_id until status is AUTHENTICATED.",
797
+ "After authentication, call browser_open with this profile name and the target site URL.",
798
+ "If the saved profile later shows login controls or disconnects, repeat browser_profile_onboard."
760
799
  ],
761
- raw: session
800
+ raw: open.data
801
+ });
802
+ }
803
+ );
804
+ server.registerTool(
805
+ "browser_profile_status",
806
+ {
807
+ title: "Check Browser Profile Auth Status",
808
+ 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.",
809
+ inputSchema: BrowserProfileStatusInputSchema,
810
+ outputSchema: BrowserProfileStatusOutputSchema,
811
+ annotations: annotations("Check Browser Profile Auth Status", true)
812
+ },
813
+ async (input) => {
814
+ const domain = slugPart(input.domain) || "chatgpt.com";
815
+ const res = await req("POST", "/agent/profiles/status", {
816
+ ...input.connection_id ? { connection_id: input.connection_id } : {},
817
+ ...input.profile ? { profile: input.profile } : {},
818
+ domain
819
+ });
820
+ if (!res.ok) return errorResult("browser_profile_status", res.data);
821
+ return structuredResult({
822
+ ok: true,
823
+ tool: "browser_profile_status",
824
+ session_id: null,
825
+ auth_connection_id: res.data.connection_id,
826
+ profile: res.data.profile,
827
+ domain: res.data.domain,
828
+ status: res.data.status,
829
+ flow_status: res.data.flow_status ?? null,
830
+ flow_step: res.data.flow_step ?? null,
831
+ flow_expires_at: res.data.flow_expires_at ?? null,
832
+ post_login_url: res.data.post_login_url ?? null,
833
+ raw: res.data
762
834
  });
763
835
  }
764
836
  );
@@ -766,7 +838,7 @@ function registerBrowserAgentMcpTools(server, opts) {
766
838
  "browser_open",
767
839
  {
768
840
  title: "Open Browser Session",
769
- 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.",
841
+ 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.",
770
842
  inputSchema: BrowserOpenInputSchema,
771
843
  outputSchema: BrowserOpenOutputSchema,
772
844
  annotations: annotations("Open Browser Session")
@@ -1188,7 +1260,7 @@ function registerBrowserAgentMcpTools(server, opts) {
1188
1260
  "browser_capture_fanout",
1189
1261
  {
1190
1262
  title: "Capture AI Search Fan-Out",
1191
- 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.`,
1263
+ 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.`,
1192
1264
  inputSchema: BrowserCaptureFanoutInputSchema,
1193
1265
  outputSchema: BrowserCaptureFanoutOutputSchema,
1194
1266
  annotations: annotations("Capture AI Search Fan-Out")
@@ -1230,4 +1302,4 @@ export {
1230
1302
  buildBrowserAgentMcpServer,
1231
1303
  registerBrowserAgentMcpTools
1232
1304
  };
1233
- //# sourceMappingURL=chunk-BRUVQRJK.js.map
1305
+ //# sourceMappingURL=chunk-QPFF3V2R.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/mcp/browser-agent-mcp-server.ts","../src/mcp/browser-agent-tool-schemas.ts","../src/mcp/replay-annotator.ts"],"sourcesContent":["import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport type { CallToolResult } from '@modelcontextprotocol/sdk/types.js'\nimport { mkdirSync, writeFileSync } from 'node:fs'\nimport { homedir } from 'node:os'\nimport { join } from 'node:path'\nimport { browserServiceProfileName, browserServiceProfileSaveChanges } from '../lib/browser-service-env.js'\nimport { PACKAGE_VERSION } from '../version.js'\nimport {\n BrowserOpenInputSchema,\n BrowserProfileOnboardInputSchema,\n BrowserProfileStatusInputSchema,\n BrowserCaptureFanoutInputSchema,\n BrowserCaptureFanoutOutputSchema,\n BrowserSessionInputSchema,\n BrowserLocateInputSchema,\n BrowserGotoInputSchema,\n BrowserClickInputSchema,\n BrowserTypeInputSchema,\n BrowserScrollInputSchema,\n BrowserPressInputSchema,\n BrowserReplayStopInputSchema,\n BrowserReplayDownloadInputSchema,\n BrowserReplayMarkInputSchema,\n BrowserReplayAnnotateInputSchema,\n BrowserListInputSchema,\n BrowserOpenOutputSchema,\n BrowserScreenshotOutputSchema,\n BrowserReadOutputSchema,\n BrowserLocateOutputSchema,\n BrowserActionOutputSchema,\n BrowserReplayStartOutputSchema,\n BrowserReplayStopOutputSchema,\n BrowserListReplaysOutputSchema,\n BrowserReplayDownloadOutputSchema,\n BrowserReplayMarkOutputSchema,\n BrowserReplayAnnotateOutputSchema,\n BrowserCloseOutputSchema,\n BrowserListSessionsOutputSchema,\n BrowserProfileOnboardOutputSchema,\n BrowserProfileStatusOutputSchema,\n} from './browser-agent-tool-schemas.js'\nimport { annotateReplayVideo } from './replay-annotator.js'\n\nexport interface BrowserAgentHttpOptions {\n baseUrl: string\n apiKey: string\n consoleBaseUrl?: string\n timeoutMs?: number\n}\n\nfunction structuredResult(value: Record<string, unknown>, isError = false): CallToolResult {\n return {\n content: [{ type: 'text', text: JSON.stringify(value) }],\n structuredContent: value,\n isError,\n }\n}\n\nfunction errorMessage(value: unknown): string {\n if (value && typeof value === 'object') {\n const data = value as Record<string, unknown>\n if (typeof data.error === 'string') return data.error\n if (typeof data.message === 'string') return data.message\n }\n return typeof value === 'string' ? value : 'Browser Agent request failed'\n}\n\nfunction errorResult(tool: string, value: unknown, sessionId: string | null = null, replayId: string | null = null): CallToolResult {\n return structuredResult({\n ok: false,\n tool,\n session_id: sessionId,\n ...(replayId !== null ? { replay_id: replayId } : {}),\n error: errorMessage(value),\n raw: value && typeof value === 'object' ? value as Record<string, unknown> : { value },\n }, true)\n}\n\nfunction actionResult(\n tool: string,\n sessionId: string,\n ok: boolean,\n data: unknown,\n nextRecommendedTool: string | null = 'browser_screenshot',\n): CallToolResult {\n if (!ok) return errorResult(tool, data, sessionId)\n return structuredResult({\n ok: true,\n tool,\n session_id: sessionId,\n result: data && typeof data === 'object' ? data as Record<string, unknown> : { value: data },\n nextRecommendedTool,\n })\n}\n\nfunction outputBaseDir(): string {\n return process.env.MCP_SCRAPER_OUTPUT_DIR?.trim() || join(homedir(), 'Downloads', 'mcp-scraper')\n}\n\nfunction safeFilePart(value: string): string {\n return value.replace(/[^a-zA-Z0-9._-]+/g, '-').replace(/^-+|-+$/g, '').slice(0, 120) || 'replay'\n}\n\nfunction replayFilePath(sessionId: string, replayId: string, filename?: string): string {\n const requested = filename?.trim()\n const stamp = new Date().toISOString().replace(/[:.]/g, '-')\n const name = requested\n ? safeFilePart(requested).replace(/\\.mp4$/i, '')\n : `${stamp}-${safeFilePart(sessionId)}-${safeFilePart(replayId)}`\n return join(outputBaseDir(), 'browser-replays', `${name}.mp4`)\n}\n\nfunction slugPart(value?: string): string | null {\n const trimmed = value?.trim().toLowerCase()\n if (!trimmed) return null\n return trimmed\n .replace(/^https?:\\/\\//, '')\n .replace(/^www\\./, '')\n .replace(/\\/.*$/, '')\n .replace(/[^a-z0-9._-]+/g, '-')\n .replace(/[._-]+/g, '-')\n .replace(/^-+|-+$/g, '')\n}\n\nfunction savedProfileNameFromEmail(email?: string, domain?: string): string | null {\n const value = slugPart(email)\n if (!value) return null\n const domainPart = slugPart(domain)\n return `${value}${domainPart ? `-${domainPart}` : ''}`.slice(0, 80) || null\n}\n\nfunction annotatedReplayFilePath(sessionId: string, replayId: string, filename?: string): string {\n const requested = filename?.trim()\n if (requested) return replayFilePath(sessionId, replayId, requested)\n const stamp = new Date().toISOString().replace(/[:.]/g, '-')\n return replayFilePath(sessionId, replayId, `${stamp}-${safeFilePart(sessionId)}-${safeFilePart(replayId)}-annotated`)\n}\n\nfunction finiteNumber(value: unknown): value is number {\n return typeof value === 'number' && Number.isFinite(value)\n}\n\nfunction expandElementBounds(\n element: { left: number; top: number; width: number; height: number },\n viewport: { width?: number; height?: number } | undefined,\n padding: number,\n) {\n const sourceWidth = finiteNumber(viewport?.width) && viewport!.width > 0 ? viewport!.width : 1920\n const sourceHeight = finiteNumber(viewport?.height) && viewport!.height > 0 ? viewport!.height : 1080\n const left = Math.max(0, element.left - padding)\n const top = Math.max(0, element.top - padding)\n const right = Math.min(sourceWidth, element.left + element.width + padding)\n const bottom = Math.min(sourceHeight, element.top + element.height + padding)\n return {\n left,\n top,\n width: Math.max(1, right - left),\n height: Math.max(1, bottom - top),\n sourceWidth,\n sourceHeight,\n }\n}\n\nexport function buildBrowserAgentMcpServer(opts: BrowserAgentHttpOptions): McpServer {\n const server = new McpServer({ name: 'browser-agent', version: PACKAGE_VERSION })\n registerBrowserAgentMcpTools(server, opts)\n return server\n}\n\nexport function registerBrowserAgentMcpTools(server: McpServer, opts: BrowserAgentHttpOptions): void {\n const baseUrl = opts.baseUrl.replace(/\\/$/, '')\n const consoleBase = (opts.consoleBaseUrl ?? opts.baseUrl).replace(/\\/$/, '')\n const timeoutMs = opts.timeoutMs ?? 90_000\n\n async function req(method: string, path: string, body?: Record<string, unknown>): Promise<{ ok: boolean; data: any }> {\n try {\n const res = await fetch(`${baseUrl}${path}`, {\n method,\n headers: { 'Content-Type': 'application/json', 'x-api-key': opts.apiKey },\n body: body ? JSON.stringify(body) : undefined,\n signal: AbortSignal.timeout(timeoutMs),\n })\n const data = await res.json().catch(() => ({}))\n return { ok: res.ok, data }\n } catch (err) {\n return { ok: false, data: { error: err instanceof Error ? err.message : String(err) } }\n }\n }\n\n async function downloadReplay(\n sessionId: string,\n replayId: string,\n filename?: string,\n ): Promise<{ ok: boolean; data: any }> {\n const path = `/agent/sessions/${encodeURIComponent(sessionId)}/replays/${encodeURIComponent(replayId)}/download`\n try {\n const res = await fetch(`${baseUrl}${path}`, {\n method: 'GET',\n headers: { 'x-api-key': opts.apiKey },\n signal: AbortSignal.timeout(timeoutMs),\n })\n if (!res.ok) {\n const data = await res.json().catch(async () => ({ error: await res.text().catch(() => `HTTP ${res.status}`) }))\n return { ok: false, data }\n }\n const bytes = Buffer.from(await res.arrayBuffer())\n const filePath = replayFilePath(sessionId, replayId, filename)\n mkdirSync(join(outputBaseDir(), 'browser-replays'), { recursive: true })\n writeFileSync(filePath, bytes)\n return {\n ok: true,\n data: {\n replay_id: replayId,\n file_path: filePath,\n bytes: bytes.length,\n mime_type: res.headers.get('content-type') ?? 'video/mp4',\n download_url: `${baseUrl}${path}`,\n },\n }\n } catch (err) {\n return { ok: false, data: { error: err instanceof Error ? err.message : String(err) } }\n }\n }\n\n const annotations = (title: string, readOnly = false) => ({\n title,\n readOnlyHint: readOnly,\n destructiveHint: false,\n idempotentHint: false,\n openWorldHint: true,\n })\n\n server.registerTool(\n 'browser_profile_onboard',\n {\n title: 'Connect Hosted Browser Profile',\n description:\n '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.',\n inputSchema: BrowserProfileOnboardInputSchema,\n outputSchema: BrowserProfileOnboardOutputSchema,\n annotations: annotations('Connect Hosted Browser Profile'),\n },\n async input => {\n const domain = slugPart(input.domain) || 'chatgpt.com'\n const setupUrl = input.login_url ?? input.url ?? (domain === 'chatgpt.com' ? 'https://chatgpt.com/' : `https://${domain}/`)\n const profile = input.profile?.trim() || savedProfileNameFromEmail(input.email, domain) || browserServiceProfileName()\n if (!profile) {\n return errorResult('browser_profile_onboard', {\n error: 'profile or email is required when BROWSER_AGENT_PROFILE_NAME is not available',\n })\n }\n const open = await req('POST', '/agent/profiles/onboard', {\n label: input.label ?? `Profile setup: ${profile}`,\n profile,\n domain,\n login_url: setupUrl,\n })\n if (!open.ok) return errorResult('browser_profile_onboard', open.data)\n return structuredResult({\n ok: true,\n tool: 'browser_profile_onboard',\n session_id: null,\n auth_connection_id: open.data.connection_id,\n watch_url: open.data.hosted_url,\n live_view_url: open.data.live_view_url ?? null,\n profile,\n domain,\n setup_url: setupUrl,\n account_email: input.email?.trim() || null,\n status: open.data.status ?? null,\n flow_status: open.data.flow_status ?? null,\n flow_step: open.data.flow_step ?? null,\n flow_expires_at: open.data.flow_expires_at ?? null,\n post_login_url: open.data.post_login_url ?? null,\n next_steps: [\n 'Open the watch_url and let the user complete login for the target domain.',\n 'Call browser_profile_status with auth_connection_id until status is AUTHENTICATED.',\n 'After authentication, call browser_open with this profile name and the target site URL.',\n 'If the saved profile later shows login controls or disconnects, repeat browser_profile_onboard.',\n ],\n raw: open.data,\n })\n },\n )\n\n server.registerTool(\n 'browser_profile_status',\n {\n title: 'Check Browser Profile Auth Status',\n description:\n '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.',\n inputSchema: BrowserProfileStatusInputSchema,\n outputSchema: BrowserProfileStatusOutputSchema,\n annotations: annotations('Check Browser Profile Auth Status', true),\n },\n async input => {\n const domain = slugPart(input.domain) || 'chatgpt.com'\n const res = await req('POST', '/agent/profiles/status', {\n ...(input.connection_id ? { connection_id: input.connection_id } : {}),\n ...(input.profile ? { profile: input.profile } : {}),\n domain,\n })\n if (!res.ok) return errorResult('browser_profile_status', res.data)\n return structuredResult({\n ok: true,\n tool: 'browser_profile_status',\n session_id: null,\n auth_connection_id: res.data.connection_id,\n profile: res.data.profile,\n domain: res.data.domain,\n status: res.data.status,\n flow_status: res.data.flow_status ?? null,\n flow_step: res.data.flow_step ?? null,\n flow_expires_at: res.data.flow_expires_at ?? null,\n post_login_url: res.data.post_login_url ?? null,\n raw: res.data,\n })\n },\n )\n\n server.registerTool(\n 'browser_open',\n {\n title: 'Open Browser Session',\n description:\n '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.',\n inputSchema: BrowserOpenInputSchema,\n outputSchema: BrowserOpenOutputSchema,\n annotations: annotations('Open Browser Session'),\n },\n async input => {\n const profile = input.profile ?? browserServiceProfileName()\n const saveProfileChanges = input.save_profile_changes ?? browserServiceProfileSaveChanges()\n const open = await req('POST', '/agent/sessions', {\n label: input.label,\n ...(profile ? { profile } : {}),\n ...(profile && typeof saveProfileChanges === 'boolean' ? { save_profile_changes: saveProfileChanges } : {}),\n disable_default_proxy: true,\n timeout_seconds: input.timeout_seconds,\n ...(input.url ? { url: input.url } : {}),\n })\n if (!open.ok) return errorResult('browser_open', open.data)\n const session = open.data\n return structuredResult({\n ok: true,\n tool: 'browser_open',\n session_id: session.session_id,\n watch_url: `${consoleBase}/console/${session.session_id}`,\n live_view_url: session.live_view_url ?? null,\n url: input.url ?? null,\n hint: 'Call browser_screenshot to see the page. Click by the x,y of an element from the snapshot.',\n raw: session,\n })\n },\n )\n\n server.registerTool(\n 'browser_screenshot',\n {\n title: 'See Page (Screenshot + Elements)',\n description:\n 'Capture what the browser currently shows. Returns a screenshot image PLUS a text snapshot listing interactive elements with their center x,y coordinates, the page url and title, and visible text. This is your primary way to perceive the page. Click elements by their listed x,y. If a Cloudflare/CAPTCHA challenge is visible, wait and screenshot again rather than clicking it.',\n inputSchema: BrowserSessionInputSchema,\n outputSchema: BrowserScreenshotOutputSchema,\n annotations: annotations('See Page', true),\n },\n async input => {\n const res = await req('POST', `/agent/sessions/${input.session_id}/screenshot`)\n if (!res.ok) return errorResult('browser_screenshot', res.data, input.session_id)\n const { image_base64, mime_type, url, title, elements, text } = res.data\n const content: CallToolResult['content'] = []\n if (image_base64) content.push({ type: 'image', data: image_base64, mimeType: mime_type ?? 'image/png' })\n const structured = {\n ok: true,\n tool: 'browser_screenshot',\n session_id: input.session_id,\n url: url ?? null,\n title: title ?? null,\n text: typeof text === 'string' ? text : '',\n elements: Array.isArray(elements) ? elements : [],\n screenshot: image_base64 ? { mime_type: mime_type ?? 'image/png', inline: true } : null,\n }\n content.push({\n type: 'text',\n text: JSON.stringify(structured),\n })\n return { content, structuredContent: structured }\n },\n )\n\n server.registerTool(\n 'browser_read',\n {\n title: 'Read Page Text + Elements',\n description:\n 'Return the page url, title, visible text, and the list of interactive elements (with x,y) without an image. Cheaper than browser_screenshot when you only need to read content or find a target element to click.',\n inputSchema: BrowserSessionInputSchema,\n outputSchema: BrowserReadOutputSchema,\n annotations: annotations('Read Page', true),\n },\n async input => {\n const res = await req('POST', `/agent/sessions/${input.session_id}/read`)\n if (!res.ok) return errorResult('browser_read', res.data, input.session_id)\n return structuredResult({\n ok: true,\n tool: 'browser_read',\n session_id: input.session_id,\n url: res.data?.url ?? null,\n title: res.data?.title ?? null,\n text: typeof res.data?.text === 'string' ? res.data.text : '',\n elements: Array.isArray(res.data?.elements) ? res.data.elements : [],\n raw: res.data,\n })\n },\n )\n\n server.registerTool(\n 'browser_locate',\n {\n title: 'Locate DOM Targets',\n description:\n 'Locate exact visible DOM elements or text ranges in the current browser viewport and return left/top/width/height bounds in screenshot pixels. Use this before drawing annotations or when a callout must literally circle, box, underline, or point to a real element. Prefer CSS selectors for exact UI elements; use text when selector is unknown. When a replay is actively recording, the result includes replay_elapsed_seconds for timing.',\n inputSchema: BrowserLocateInputSchema,\n outputSchema: BrowserLocateOutputSchema,\n annotations: annotations('Locate DOM Targets', true),\n },\n async input => {\n const res = await req('POST', `/agent/sessions/${input.session_id}/locate`, { targets: input.targets })\n if (!res.ok) return errorResult('browser_locate', res.data, input.session_id)\n return structuredResult({\n ok: true,\n tool: 'browser_locate',\n session_id: input.session_id,\n url: res.data?.url ?? null,\n title: res.data?.title ?? null,\n viewport: res.data?.viewport ?? null,\n replay: res.data?.replay ?? null,\n targets: Array.isArray(res.data?.targets) ? res.data.targets : [],\n raw: res.data,\n })\n },\n )\n\n server.registerTool(\n 'browser_goto',\n {\n title: 'Navigate To URL',\n description: 'Navigate an existing browser session to a URL when the user asks you to go to another page or continue browsing in the same session. Use browser_open first if no session exists. Follow with browser_screenshot to see redirects, login walls, CAPTCHA/Cloudflare state, or the loaded page.',\n inputSchema: BrowserGotoInputSchema,\n outputSchema: BrowserActionOutputSchema,\n annotations: annotations('Navigate To URL'),\n },\n async input => {\n const res = await req('POST', `/agent/sessions/${input.session_id}/goto`, { url: input.url })\n return actionResult('browser_goto', input.session_id, res.ok, res.data, 'browser_screenshot')\n },\n )\n\n server.registerTool(\n 'browser_click',\n {\n title: 'Click',\n description: 'Click a visible page target using screenshot pixel coordinates. Use this when the user asks you to press a button, open a menu, choose a result, or activate a visible UI element. Use x/y only from the latest browser_screenshot, browser_read, or browser_locate result; do not guess coordinates. Follow with browser_screenshot when the click may change the page.',\n inputSchema: BrowserClickInputSchema,\n outputSchema: BrowserActionOutputSchema,\n annotations: annotations('Click'),\n },\n async input => {\n const res = await req('POST', `/agent/sessions/${input.session_id}/click`, {\n x: input.x,\n y: input.y,\n button: input.button,\n num_clicks: input.num_clicks,\n })\n return actionResult('browser_click', input.session_id, res.ok, res.data, 'browser_screenshot')\n },\n )\n\n server.registerTool(\n 'browser_type',\n {\n title: 'Type Text',\n description: 'Type text into the currently focused browser field. Use this when the user asks you to fill a search box, form field, login field, or editable text area. Click or Tab to the field first if focus is uncertain. Use browser_press with [\"Return\"] to submit, and browser_screenshot afterward when page state matters.',\n inputSchema: BrowserTypeInputSchema,\n outputSchema: BrowserActionOutputSchema,\n annotations: annotations('Type Text'),\n },\n async input => {\n const res = await req('POST', `/agent/sessions/${input.session_id}/type`, { text: input.text, delay: input.delay })\n return actionResult('browser_type', input.session_id, res.ok, res.data, 'browser_screenshot')\n },\n )\n\n server.registerTool(\n 'browser_scroll',\n {\n title: 'Scroll',\n description: 'Scroll the page to reveal more content before reading, clicking, or locating elements. Positive delta_y scrolls down; negative delta_y scrolls up. Follow with browser_screenshot or browser_read to inspect newly revealed content.',\n inputSchema: BrowserScrollInputSchema,\n outputSchema: BrowserActionOutputSchema,\n annotations: annotations('Scroll'),\n },\n async input => {\n const res = await req('POST', `/agent/sessions/${input.session_id}/scroll`, {\n delta_y: input.delta_y,\n delta_x: input.delta_x,\n x: input.x,\n y: input.y,\n })\n return actionResult('browser_scroll', input.session_id, res.ok, res.data, 'browser_screenshot')\n },\n )\n\n server.registerTool(\n 'browser_press',\n {\n title: 'Press Keys',\n description: 'Press keyboard keys or combinations in the active browser session. Use this for submit, Escape, Tab navigation, select-all, or keyboard shortcuts. Examples: [\"Return\"], [\"Escape\"], [\"Tab\"], [\"Ctrl+a\"], [\"Ctrl+Shift+Tab\"]. Use browser_type for text entry and browser_screenshot after keypresses that may change the page.',\n inputSchema: BrowserPressInputSchema,\n outputSchema: BrowserActionOutputSchema,\n annotations: annotations('Press Keys'),\n },\n async input => {\n const res = await req('POST', `/agent/sessions/${input.session_id}/press`, { keys: input.keys })\n return actionResult('browser_press', input.session_id, res.ok, res.data, 'browser_screenshot')\n },\n )\n\n server.registerTool(\n 'browser_replay_start',\n {\n title: 'Start Recording',\n description: 'Start recording an MP4 replay of the session. Returns replay_id, view_url when available, and a download_url. Use to capture a task for later review; stop with browser_replay_stop.',\n inputSchema: BrowserSessionInputSchema,\n outputSchema: BrowserReplayStartOutputSchema,\n annotations: annotations('Start Recording'),\n },\n async input => {\n const res = await req('POST', `/agent/sessions/${input.session_id}/replay/start`)\n if (!res.ok) return errorResult('browser_replay_start', res.data, input.session_id)\n return structuredResult({\n ok: true,\n tool: 'browser_replay_start',\n session_id: input.session_id,\n replay_id: res.data?.replay_id ?? res.data?.replayId ?? null,\n view_url: res.data?.view_url ?? res.data?.viewUrl ?? null,\n download_url: res.data?.download_url ?? res.data?.downloadUrl ?? null,\n raw: res.data,\n })\n },\n )\n\n server.registerTool(\n 'browser_replay_stop',\n {\n title: 'Stop Recording',\n description: 'Stop a replay recording and expose the final view_url and download_url. Use browser_replay_download to save the MP4 to the configured output directory.',\n inputSchema: BrowserReplayStopInputSchema,\n outputSchema: BrowserReplayStopOutputSchema,\n annotations: annotations('Stop Recording'),\n },\n async input => {\n const res = await req('POST', `/agent/sessions/${input.session_id}/replay/stop`, { replay_id: input.replay_id })\n if (!res.ok) return errorResult('browser_replay_stop', res.data, input.session_id, input.replay_id)\n return structuredResult({\n ok: true,\n tool: 'browser_replay_stop',\n session_id: input.session_id,\n replay_id: res.data?.replay_id ?? res.data?.replayId ?? input.replay_id,\n view_url: res.data?.view_url ?? res.data?.viewUrl ?? null,\n download_url: res.data?.download_url ?? res.data?.downloadUrl ?? null,\n raw: res.data,\n })\n },\n )\n\n server.registerTool(\n 'browser_list_replays',\n {\n title: 'List Replay Videos',\n description: 'List replay recordings for a browser session, including final view_url and authenticated download_url values when available.',\n inputSchema: BrowserSessionInputSchema,\n outputSchema: BrowserListReplaysOutputSchema,\n annotations: annotations('List Replay Videos', true),\n },\n async input => {\n const res = await req('GET', `/agent/sessions/${input.session_id}/replays`)\n if (!res.ok) return errorResult('browser_list_replays', res.data, input.session_id)\n const replays = Array.isArray(res.data?.replays) ? res.data.replays : []\n return structuredResult({\n ok: true,\n tool: 'browser_list_replays',\n session_id: input.session_id,\n replays,\n count: replays.length,\n })\n },\n )\n\n server.registerTool(\n 'browser_replay_download',\n {\n title: 'Download Replay MP4',\n description: 'Download a replay recording through MCP Scraper and save the MP4 under MCP_SCRAPER_OUTPUT_DIR/browser-replays. Use after browser_replay_stop or browser_list_replays.',\n inputSchema: BrowserReplayDownloadInputSchema,\n outputSchema: BrowserReplayDownloadOutputSchema,\n annotations: annotations('Download Replay MP4'),\n },\n async input => {\n const res = await downloadReplay(input.session_id, input.replay_id, input.filename)\n if (!res.ok) return errorResult('browser_replay_download', res.data, input.session_id, input.replay_id)\n return structuredResult({\n ok: true,\n tool: 'browser_replay_download',\n session_id: input.session_id,\n replay_id: input.replay_id,\n file_path: res.data?.file_path ?? null,\n bytes: typeof res.data?.bytes === 'number' ? res.data.bytes : null,\n mime_type: res.data?.mime_type ?? null,\n download_url: res.data?.download_url ?? null,\n })\n },\n )\n\n server.registerTool(\n 'browser_replay_mark',\n {\n title: 'Mark Replay Annotation',\n description:\n 'While a replay is actively recording, locate one exact DOM target and return a ready-to-use annotation object with DOM bounds and replay-relative timing. Use this instead of guessing start_seconds or drawing rough rectangles. Workflow: start browser_replay_start, navigate until the target is visible and stable, call browser_replay_mark for each callout, then stop the replay and pass the returned annotations to browser_replay_annotate.',\n inputSchema: BrowserReplayMarkInputSchema,\n outputSchema: BrowserReplayMarkOutputSchema,\n annotations: annotations('Mark Replay Annotation', true),\n },\n async input => {\n const res = await req('POST', `/agent/sessions/${input.session_id}/locate`, { targets: [input.target] })\n if (!res.ok) return errorResult('browser_replay_mark', res.data, input.session_id)\n const target = res.data?.targets?.[0]\n const element = target?.element\n const elapsed = res.data?.replay?.replay_elapsed_seconds\n if (!target?.found || !element) {\n return errorResult('browser_replay_mark', { error: target?.error ?? 'target not found in current viewport', target }, input.session_id)\n }\n if (!finiteNumber(elapsed)) {\n return errorResult('browser_replay_mark', { error: 'no active replay clock found; call browser_replay_start before browser_replay_mark' }, input.session_id)\n }\n const padded = expandElementBounds(element, res.data?.viewport, input.padding ?? 8)\n const start = Math.max(0, elapsed + (input.start_offset_seconds ?? -0.25))\n const duration = input.duration_seconds ?? 4\n const annotation = {\n type: input.type ?? 'box',\n start_seconds: Number(start.toFixed(3)),\n end_seconds: Number((start + duration).toFixed(3)),\n left: Math.round(padded.left),\n top: Math.round(padded.top),\n width: Math.round(padded.width),\n height: Math.round(padded.height),\n ...(input.label ? { label: input.label } : {}),\n ...(input.color ? { color: input.color } : {}),\n ...(input.thickness ? { thickness: input.thickness } : {}),\n }\n return structuredResult({\n ok: true,\n tool: 'browser_replay_mark',\n session_id: input.session_id,\n replay_id: res.data?.replay?.replay_id ?? res.data?.replay?.replayId ?? null,\n annotation,\n source_width: padded.sourceWidth,\n source_height: padded.sourceHeight,\n replay: res.data.replay,\n target,\n hint: 'Append annotation to your annotations array. Use source_width/source_height with browser_replay_annotate.',\n })\n },\n )\n\n server.registerTool(\n 'browser_replay_annotate',\n {\n title: 'Annotate Replay MP4',\n description:\n 'Download a browser replay MP4, render visual annotations over it, and save a new annotated MP4 under MCP_SCRAPER_OUTPUT_DIR/browser-replays. Use this after browser_replay_stop when the user wants proof videos with circles, boxes, arrows, underlines, or labels. For accurate timing and placement, prefer annotations returned by browser_replay_mark while the replay is recording; otherwise use exact left/top/width/height bounds from browser_locate. If the replay video size differs from the screenshot coordinate space, pass source_width and source_height.',\n inputSchema: BrowserReplayAnnotateInputSchema,\n outputSchema: BrowserReplayAnnotateOutputSchema,\n annotations: annotations('Annotate Replay MP4'),\n },\n async input => {\n const sourceName = input.filename ? `${input.filename}-source` : undefined\n const downloaded = await downloadReplay(input.session_id, input.replay_id, sourceName)\n if (!downloaded.ok) return errorResult('browser_replay_annotate', downloaded.data, input.session_id, input.replay_id)\n try {\n const sourcePath = String(downloaded.data.file_path)\n const outputPath = annotatedReplayFilePath(input.session_id, input.replay_id, input.filename)\n mkdirSync(join(outputBaseDir(), 'browser-replays'), { recursive: true })\n const result = await annotateReplayVideo(sourcePath, outputPath, {\n annotations: input.annotations,\n sourceWidth: input.source_width,\n sourceHeight: input.source_height,\n sourceLeftOffset: input.source_left_offset,\n sourceTopOffset: input.source_top_offset,\n })\n return structuredResult({\n ok: true,\n tool: 'browser_replay_annotate',\n session_id: input.session_id,\n replay_id: input.replay_id,\n source_file_path: sourcePath,\n annotated_file_path: result.filePath,\n bytes: result.bytes,\n width: result.width,\n height: result.height,\n annotation_count: result.annotationCount,\n mime_type: 'video/mp4',\n })\n } catch (err) {\n return errorResult('browser_replay_annotate', { error: err instanceof Error ? err.message : String(err) }, input.session_id, input.replay_id)\n }\n },\n )\n\n server.registerTool(\n 'browser_close',\n {\n title: 'Close Browser Session',\n description: 'Close and release a browser session when the task is done, when the user asks to stop the browser, or when active browser billing should end. Use browser_list_sessions first if you need to recover the session_id.',\n inputSchema: BrowserSessionInputSchema,\n outputSchema: BrowserCloseOutputSchema,\n annotations: { title: 'Close Browser Session', readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: false },\n },\n async input => {\n const res = await req('DELETE', `/agent/sessions/${input.session_id}`)\n if (!res.ok) return errorResult('browser_close', res.data, input.session_id)\n return structuredResult({\n ok: true,\n tool: 'browser_close',\n session_id: input.session_id,\n closed: true,\n raw: res.data,\n })\n },\n )\n\n server.registerTool(\n 'browser_list_sessions',\n {\n title: 'List Browser Sessions',\n description: 'List browser sessions and their status, with a watch_url for each. Use this to recover a session_id, find an active browser, or decide which session to close. Use browser_open to create a new session.',\n inputSchema: BrowserListInputSchema,\n outputSchema: BrowserListSessionsOutputSchema,\n annotations: annotations('List Browser Sessions', true),\n },\n async input => {\n const res = await req('GET', `/agent/sessions${input.include_closed ? '?all=1' : ''}`)\n if (!res.ok) return errorResult('browser_list_sessions', res.data)\n const sessions = (res.data.sessions ?? []).map((s: any) => ({ ...s, watch_url: `${consoleBase}/console/${s.session_id}` }))\n return structuredResult({\n ok: true,\n tool: 'browser_list_sessions',\n session_id: null,\n sessions,\n count: sessions.length,\n })\n },\n )\n\n server.registerTool(\n 'browser_capture_fanout',\n {\n title: 'Capture AI Search Fan-Out',\n description:\n '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 — 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) — 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 — use harvest_paa for those; this tool is ChatGPT and Claude only.',\n inputSchema: BrowserCaptureFanoutInputSchema,\n outputSchema: BrowserCaptureFanoutOutputSchema,\n annotations: annotations('Capture AI Search Fan-Out'),\n },\n async input => {\n const emit = (result: Record<string, unknown>, exports: unknown) => structuredResult({\n ok: true,\n tool: 'browser_capture_fanout',\n session_id: input.session_id,\n platform: result.platform,\n captured_at: result.capturedAt,\n prompt: result.prompt,\n meta: result.meta,\n queries: result.queries,\n browsed_urls: result.browsedUrls,\n cited_urls: result.citedUrls,\n browsed_only: result.browsedOnly,\n snippets: result.snippets,\n counts: result.counts,\n aggregates: result.aggregates,\n first_party_domain: result.firstPartyDomain,\n exports: exports ?? null,\n ...(result.debug ? { debug: result.debug } : {}),\n })\n const res = await req('POST', `/agent/sessions/${input.session_id}/capture-fanout`, {\n prompt: input.prompt,\n wait_ms: input.wait_ms,\n first_party_domain: input.first_party_domain,\n export: false,\n })\n if (!res.ok) return errorResult('browser_capture_fanout', res.data, input.session_id)\n const hosted = (res.data?.result ?? res.data) as Record<string, unknown>\n return emit(hosted, res.data?.exports ?? null)\n },\n )\n\n}\n","import { z } from 'zod'\n\nconst NullableString = z.string().nullable()\nconst BrowserRawObject = z.record(z.unknown())\n\nconst BrowserElementOutput = z.record(z.unknown())\n\nconst BrowserBaseOutput = {\n ok: z.boolean().describe('Whether the browser-agent action succeeded.'),\n tool: z.string().describe('Browser Agent MCP tool that produced this response.'),\n session_id: NullableString.describe('Browser session id when the response is scoped to a session.'),\n}\n\nconst BrowserReplayBaseOutput = {\n ...BrowserBaseOutput,\n replay_id: NullableString.describe('Replay id when the response is scoped to a replay.'),\n}\n\nexport const BrowserOpenInputSchema = {\n label: z.string().optional().describe('Optional human label for this session, shown in the watch console.'),\n url: z.string().url().optional().describe('Optional URL to navigate to immediately after opening.'),\n profile: z.string().optional().describe('Optional saved hosted profile name to load a logged-in session for a site.'),\n save_profile_changes: z.boolean().optional().describe('Persist cookies and browser storage back to the named profile when the session is closed. Use this for profile setup or intentional auth refreshes; avoid parallel sessions writing to the same profile.'),\n timeout_seconds: z.number().int().min(60).max(259200).optional().describe('How long the direct no-proxy browser session may live before auto-termination. Defaults to 600. The browser idles into a zero-cost standby between actions, so a longer timeout is cheap.'),\n}\nexport type BrowserOpenInput = z.infer<ReturnType<typeof z.object<typeof BrowserOpenInputSchema>>>\n\nexport const BrowserProfileOnboardInputSchema = {\n email: z.string().optional().describe('Optional account email used to derive a stable MCP Scraper profile name. This does not import cookies or browser state.'),\n profile: z.string().optional().describe('Optional saved MCP Scraper hosted browser profile name to create or load. If omitted, email is used to derive one.'),\n domain: z.string().optional().describe('Authenticated site domain to connect to the profile. Defaults to chatgpt.com for AI visibility fan-out tracking.'),\n login_url: z.string().url().optional().describe('Optional login URL for the target domain. Defaults to https://chatgpt.com/ when domain is omitted.'),\n url: z.string().url().optional().describe('Deprecated alias for login_url. Use login_url for the target site login page.'),\n label: z.string().optional().describe('Optional human label for this setup session.'),\n timeout_seconds: 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.'),\n}\nexport type BrowserProfileOnboardInput = z.infer<ReturnType<typeof z.object<typeof BrowserProfileOnboardInputSchema>>>\n\nexport const BrowserProfileStatusInputSchema = {\n connection_id: z.string().optional().describe('Auth connection id returned by browser_profile_onboard. Prefer this when available.'),\n profile: z.string().optional().describe('Saved hosted profile name to check when connection_id is unavailable.'),\n domain: z.string().optional().describe('Authenticated site domain to check with profile. Defaults to chatgpt.com when omitted.'),\n}\nexport type BrowserProfileStatusInput = z.infer<ReturnType<typeof z.object<typeof BrowserProfileStatusInputSchema>>>\n\nexport const BrowserSessionInputSchema = {\n session_id: 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.'),\n}\nexport type BrowserSessionInput = z.infer<ReturnType<typeof z.object<typeof BrowserSessionInputSchema>>>\n\nexport const BrowserLocateTargetSchema = z.object({\n name: z.string().optional().describe('Optional label for this target, echoed in the result.'),\n selector: z.string().optional().describe('CSS selector for the exact DOM element to locate, for example h1, input[name=\"q\"], or [data-testid=\"result\"].'),\n text: z.string().optional().describe('Visible text to locate when a selector is not known. The tool returns the text range bounds when possible.'),\n match: z.enum(['contains', 'exact']).default('contains').describe('How to match text targets. Defaults to contains.'),\n index: z.number().int().min(0).default(0).describe('Zero-based match index when multiple elements match.'),\n}).refine(value => Boolean(value.selector || value.text), {\n message: 'target requires selector or text',\n})\n\nexport const BrowserLocateInputSchema = {\n session_id: 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.'),\n targets: z.array(BrowserLocateTargetSchema).min(1).max(20).describe('DOM targets to locate in the current viewport. Use selectors for exact elements, or text for visible text ranges.'),\n}\nexport type BrowserLocateInput = z.infer<ReturnType<typeof z.object<typeof BrowserLocateInputSchema>>>\n\nexport const BrowserGotoInputSchema = {\n session_id: 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.'),\n url: z.string().url().describe('URL to navigate the browser to.'),\n}\nexport type BrowserGotoInput = z.infer<ReturnType<typeof z.object<typeof BrowserGotoInputSchema>>>\n\nexport const BrowserClickInputSchema = {\n session_id: 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.'),\n x: z.number().describe('X coordinate to click, in screenshot pixels. Use only coordinates from the latest browser_screenshot, browser_read, or browser_locate result; do not guess.'),\n y: z.number().describe('Y coordinate to click, in screenshot pixels. Use only coordinates from the latest browser_screenshot, browser_read, or browser_locate result; do not guess.'),\n button: z.enum(['left', 'right', 'middle']).default('left').describe('Mouse button.'),\n num_clicks: z.number().int().min(1).max(3).optional().describe('Number of clicks, e.g. 2 for double-click.'),\n}\nexport type BrowserClickInput = z.infer<ReturnType<typeof z.object<typeof BrowserClickInputSchema>>>\n\nexport const BrowserTypeInputSchema = {\n session_id: 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.'),\n text: z.string().describe('Text to type at the current focus. Click a field first to focus it.'),\n delay: z.number().int().min(0).max(500).optional().describe('Optional per-keystroke delay in ms for human-like typing.'),\n}\nexport type BrowserTypeInput = z.infer<ReturnType<typeof z.object<typeof BrowserTypeInputSchema>>>\n\nexport const BrowserScrollInputSchema = {\n session_id: 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.'),\n delta_y: z.number().default(5).describe('Vertical scroll in wheel units. Positive scrolls down, negative up.'),\n delta_x: z.number().default(0).describe('Horizontal scroll in wheel units.'),\n x: z.number().optional().describe('X position to scroll at. Defaults to screen center.'),\n y: z.number().optional().describe('Y position to scroll at. Defaults to screen center.'),\n}\nexport type BrowserScrollInput = z.infer<ReturnType<typeof z.object<typeof BrowserScrollInputSchema>>>\n\nexport const BrowserPressInputSchema = {\n session_id: 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.'),\n keys: z.array(z.string()).min(1).describe('Keys or combinations to press, e.g. [\"Return\"], [\"Ctrl+a\"], [\"Ctrl+Shift+Tab\"].'),\n}\nexport type BrowserPressInput = z.infer<ReturnType<typeof z.object<typeof BrowserPressInputSchema>>>\n\nexport const BrowserReplayStopInputSchema = {\n session_id: 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.'),\n replay_id: z.string().describe('The replay id returned by browser_replay_start or browser_list_replays. Use only a returned replay_id; do not construct one yourself.'),\n}\nexport type BrowserReplayStopInput = z.infer<ReturnType<typeof z.object<typeof BrowserReplayStopInputSchema>>>\n\nexport const BrowserReplayDownloadInputSchema = {\n session_id: 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.'),\n replay_id: z.string().describe('The replay id returned by browser_replay_start or browser_list_replays. Use only a returned replay_id; do not construct one yourself.'),\n filename: z.string().optional().describe('Optional local MP4 filename. Defaults to a timestamped replay filename.'),\n}\nexport type BrowserReplayDownloadInput = z.infer<ReturnType<typeof z.object<typeof BrowserReplayDownloadInputSchema>>>\n\nexport const BrowserReplayAnnotationSchema = z.object({\n type: z.enum(['box', 'circle', 'underline', 'arrow', 'label']).default('box').describe('Annotation style to draw over the replay.'),\n start_seconds: z.number().min(0).default(0).describe('When the annotation should appear in the replay.'),\n end_seconds: z.number().min(0).optional().describe('When the annotation should disappear. Defaults to two seconds after start_seconds.'),\n left: z.number().optional().describe('Target left edge in browser screenshot pixels. Use element.left from browser_screenshot/browser_read.'),\n top: z.number().optional().describe('Target top edge in browser screenshot pixels. Use element.top from browser_screenshot/browser_read.'),\n width: z.number().positive().optional().describe('Target width in browser screenshot pixels. Use element.width from browser_screenshot/browser_read.'),\n height: z.number().positive().optional().describe('Target height in browser screenshot pixels. Use element.height from browser_screenshot/browser_read.'),\n x: z.number().optional().describe('Point target x coordinate in browser screenshot pixels when no box is available.'),\n y: z.number().optional().describe('Point target y coordinate in browser screenshot pixels when no box is available.'),\n from_x: z.number().optional().describe('Arrow start x coordinate in browser screenshot pixels. Defaults near the target.'),\n from_y: z.number().optional().describe('Arrow start y coordinate in browser screenshot pixels. Defaults near the target.'),\n to_x: z.number().optional().describe('Arrow target x coordinate in browser screenshot pixels. Defaults to the target box center.'),\n to_y: z.number().optional().describe('Arrow target y coordinate in browser screenshot pixels. Defaults to the target box center.'),\n label: z.string().max(120).optional().describe('Optional text callout to render near the annotation.'),\n color: z.string().regex(/^#?[0-9a-fA-F]{6}$/).optional().describe('Annotation color as hex, for example #ff3b30.'),\n thickness: z.number().min(1).max(24).optional().describe('Stroke thickness in pixels. Defaults to 5.'),\n})\n\nexport const BrowserReplayMarkInputSchema = {\n session_id: 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. A replay must already be recording.'),\n target: BrowserLocateTargetSchema.describe('The exact DOM element or text range to mark in the current viewport.'),\n type: z.enum(['box', 'circle', 'underline', 'arrow']).default('box').describe('Annotation style to generate. Labels are included on the returned annotation when label is provided.'),\n label: z.string().max(120).optional().describe('Optional callout text to render near the target.'),\n color: z.string().regex(/^#?[0-9a-fA-F]{6}$/).optional().describe('Annotation color as hex, for example #ff3b30.'),\n thickness: z.number().min(1).max(24).optional().describe('Stroke thickness in pixels. Defaults to 5.'),\n padding: z.number().min(0).max(80).default(8).describe('Pixels to expand the DOM bounds so the highlight does not touch the text edge.'),\n start_offset_seconds: z.number().min(-5).max(10).default(-0.25).describe('Offset from the current replay time. Negative values make the callout appear just before the mark action is captured.'),\n duration_seconds: z.number().min(0.5).max(30).default(4).describe('How long the generated annotation should remain visible.'),\n}\nexport type BrowserReplayMarkInput = z.infer<ReturnType<typeof z.object<typeof BrowserReplayMarkInputSchema>>>\n\nexport const BrowserReplayAnnotateInputSchema = {\n session_id: 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.'),\n replay_id: z.string().describe('The replay id returned by browser_replay_start or browser_list_replays. Use only a returned replay_id; do not construct one yourself.'),\n annotations: z.array(BrowserReplayAnnotationSchema).min(1).max(50).describe('Timed overlay annotations to render on the replay. Prefer annotations returned by browser_replay_mark; otherwise use exact DOM bounds from browser_locate/browser_screenshot/browser_read.'),\n filename: z.string().optional().describe('Optional output MP4 filename. Defaults to a timestamped annotated replay filename.'),\n source_width: z.number().positive().optional().describe('Width of the screenshot coordinate space used for annotations. Defaults to the replay video width.'),\n source_height: z.number().positive().optional().describe('Height of the coordinate space used for annotations. When this is a page viewport height smaller than the replay video height, the annotator infers the browser chrome top offset.'),\n source_left_offset: z.number().min(0).optional().describe('Optional explicit X offset from annotation coordinates to replay video coordinates. Usually omitted.'),\n source_top_offset: z.number().min(0).optional().describe('Optional explicit Y offset from annotation coordinates to replay video coordinates. Usually omitted because browser chrome offset is inferred when source_height is a page viewport height.'),\n}\nexport type BrowserReplayAnnotateInput = z.infer<ReturnType<typeof z.object<typeof BrowserReplayAnnotateInputSchema>>>\n\nexport const BrowserListInputSchema = {\n include_closed: z.boolean().default(false).describe('Include closed sessions in the list.'),\n}\nexport type BrowserListInput = z.infer<ReturnType<typeof z.object<typeof BrowserListInputSchema>>>\n\nexport const BrowserCaptureFanoutInputSchema = {\n session_id: z.string().describe('The session id returned by browser_open or browser_list_sessions. The session must be on chatgpt.com or claude.ai and logged in through a saved hosted profile. Use only a returned session_id; do not construct one yourself.'),\n prompt: z.string().optional().describe('Optional prompt to type into the chat composer and submit before capturing. Omit to passively capture the fan-out of a prompt the user just ran in the visible browser. The prompt must trigger web search for there to be a fan-out.'),\n wait_ms: z.number().int().min(0).max(180000).optional().describe('How long to wait for the answer stream to finish before parsing. Defaults to 90000 when a prompt is sent, 8000 for passive capture. Capture ends as soon as the answer stream finishes.'),\n first_party_domain: z.string().optional().describe('The brand/site being researched, for example example.com. Sources on this domain are tagged First-party/vendor. Set this when the prompt is about a specific business.'),\n reset: z.boolean().default(false).describe('Clear any previously buffered stream for this page before capturing. Use when re-running to avoid mixing turns.'),\n export: z.boolean().default(false).describe('When true, write JSON, CSV, TSV exports (queries, citations, sources, domains) and a self-contained HTML report to MCP_SCRAPER_OUTPUT_DIR/fanout and return their paths.'),\n}\nexport type BrowserCaptureFanoutInput = z.infer<ReturnType<typeof z.object<typeof BrowserCaptureFanoutInputSchema>>>\n\nconst FanoutSourceOutput = z.object({\n url: z.string(),\n domain: z.string(),\n title: z.string(),\n cited: z.boolean(),\n timesCited: z.number().int().min(0),\n snippet: z.string(),\n round: z.number().int().nullable(),\n siteType: z.string().describe('URL category: First-party/vendor, News/media, Reddit, Social/video, Encyclopedia, Review site, Docs, or Blog.'),\n})\n\nexport const BrowserCaptureFanoutOutputSchema = {\n ...BrowserBaseOutput,\n tool: z.literal('browser_capture_fanout'),\n platform: z.enum(['ChatGPT', 'Claude']).describe('Which AI-search surface the fan-out was captured from.'),\n captured_at: z.string(),\n prompt: z.string().describe('The user prompt that triggered the captured fan-out, when recoverable.'),\n meta: z.object({\n model: z.string(),\n finishType: z.string(),\n title: z.string(),\n rounds: z.number().int().min(0),\n }),\n queries: z.array(z.string()).describe('Every web-search sub-query the model issued, in capture order. Classify these by funnel stage and type yourself from the text.'),\n browsed_urls: z.array(FanoutSourceOutput).describe('Every researched URL (cited and browsed-only), cited first.'),\n cited_urls: z.array(FanoutSourceOutput).describe('The subset of researched URLs actually cited in the final answer.'),\n browsed_only: z.array(FanoutSourceOutput).describe('Researched URLs the model pulled but did not cite.'),\n snippets: z.array(z.object({ url: z.string(), domain: z.string(), title: z.string(), text: z.string() })),\n counts: z.object({\n subQueries: z.number().int().min(0),\n browsed: z.number().int().min(0),\n cited: z.number().int().min(0),\n browsedOnly: z.number().int().min(0),\n }),\n aggregates: z.object({\n topSites: z.array(z.object({ domain: z.string(), count: z.number().int(), cited: z.boolean(), timesCited: z.number().int(), siteType: z.string() })),\n citationOrder: z.array(z.object({ rank: z.number().int(), domain: z.string(), url: z.string(), timesCited: z.number().int() })),\n byCategory: z.record(z.number().int()),\n }).describe('Objective aggregates: top sourced sites by frequency, citation order, and URL-category counts.'),\n first_party_domain: z.string().nullable(),\n exports: z.object({\n dir: z.string(),\n json: z.string(),\n queriesCsv: z.string(),\n queriesTsv: z.string(),\n citationsCsv: z.string(),\n sourcesCsv: z.string(),\n domainsCsv: z.string(),\n report: z.string(),\n }).nullable().describe('Local file paths when export=true, otherwise null.'),\n debug: z.object({\n interceptorReady: z.boolean(),\n rawBytes: z.number().int().min(0),\n unmappedKeys: z.array(z.string()),\n note: z.string(),\n }).optional(),\n}\n\nexport const BrowserOpenOutputSchema = {\n ok: z.boolean(),\n tool: z.literal('browser_open'),\n session_id: z.string().describe('Session id returned by browser_open. Use only this exact value in later browser_* calls; do not construct one yourself.'),\n watch_url: z.string().describe('Human watch/takeover URL for this browser session.'),\n live_view_url: NullableString.describe('Raw live-view URL from the browser service when available.'),\n url: NullableString.describe('Initial URL requested by the caller, when provided.'),\n hint: z.string(),\n raw: BrowserRawObject.optional(),\n}\n\nexport const BrowserProfileOnboardOutputSchema = {\n ok: z.boolean(),\n tool: z.literal('browser_profile_onboard'),\n session_id: NullableString,\n auth_connection_id: z.string(),\n watch_url: z.string(),\n live_view_url: NullableString,\n profile: z.string(),\n domain: z.string(),\n setup_url: z.string(),\n account_email: NullableString,\n status: z.string(),\n flow_status: NullableString,\n flow_step: NullableString,\n flow_expires_at: NullableString,\n post_login_url: NullableString,\n next_steps: z.array(z.string()),\n raw: BrowserRawObject.optional(),\n}\n\nexport const BrowserProfileStatusOutputSchema = {\n ok: z.boolean(),\n tool: z.literal('browser_profile_status'),\n session_id: NullableString,\n auth_connection_id: z.string(),\n profile: z.string(),\n domain: z.string(),\n status: z.string(),\n flow_status: NullableString,\n flow_step: NullableString,\n flow_expires_at: NullableString,\n post_login_url: NullableString,\n raw: BrowserRawObject.optional(),\n}\n\nexport const BrowserScreenshotOutputSchema = {\n ...BrowserBaseOutput,\n tool: z.literal('browser_screenshot'),\n url: NullableString,\n title: NullableString,\n text: z.string(),\n elements: z.array(BrowserElementOutput),\n screenshot: z.object({\n mime_type: z.string(),\n inline: z.boolean(),\n }).nullable(),\n}\n\nexport const BrowserReadOutputSchema = {\n ...BrowserBaseOutput,\n tool: z.literal('browser_read'),\n url: NullableString,\n title: NullableString,\n text: z.string(),\n elements: z.array(BrowserElementOutput),\n raw: BrowserRawObject.optional(),\n}\n\nexport const BrowserLocateOutputSchema = {\n ...BrowserBaseOutput,\n tool: z.literal('browser_locate'),\n url: NullableString,\n title: NullableString,\n viewport: BrowserRawObject.nullable(),\n replay: BrowserRawObject.nullable(),\n targets: z.array(BrowserRawObject),\n raw: BrowserRawObject.optional(),\n}\n\nexport const BrowserActionOutputSchema = {\n ...BrowserBaseOutput,\n result: BrowserRawObject.describe('Provider action result. Check ok and follow with browser_screenshot/browser_read when page state matters.'),\n nextRecommendedTool: z.string().nullable(),\n}\n\nexport const BrowserReplayStartOutputSchema = {\n ...BrowserReplayBaseOutput,\n tool: z.literal('browser_replay_start'),\n view_url: NullableString,\n download_url: NullableString,\n raw: BrowserRawObject.optional(),\n}\n\nexport const BrowserReplayStopOutputSchema = {\n ...BrowserReplayBaseOutput,\n tool: z.literal('browser_replay_stop'),\n view_url: NullableString,\n download_url: NullableString,\n raw: BrowserRawObject.optional(),\n}\n\nexport const BrowserListReplaysOutputSchema = {\n ...BrowserBaseOutput,\n tool: z.literal('browser_list_replays'),\n replays: z.array(BrowserRawObject),\n count: z.number().int().min(0),\n}\n\nexport const BrowserReplayDownloadOutputSchema = {\n ...BrowserReplayBaseOutput,\n tool: z.literal('browser_replay_download'),\n file_path: NullableString,\n bytes: z.number().int().min(0).nullable(),\n mime_type: NullableString,\n download_url: NullableString,\n}\n\nexport const BrowserReplayMarkOutputSchema = {\n ...BrowserReplayBaseOutput,\n tool: z.literal('browser_replay_mark'),\n annotation: BrowserRawObject,\n source_width: z.number().nullable(),\n source_height: z.number().nullable(),\n target: BrowserRawObject.nullable(),\n hint: z.string(),\n}\n\nexport const BrowserReplayAnnotateOutputSchema = {\n ...BrowserReplayBaseOutput,\n tool: z.literal('browser_replay_annotate'),\n source_file_path: NullableString,\n annotated_file_path: NullableString,\n bytes: z.number().int().min(0).nullable(),\n width: z.number().int().min(0).nullable(),\n height: z.number().int().min(0).nullable(),\n annotation_count: z.number().int().min(0).nullable(),\n mime_type: NullableString,\n}\n\nexport const BrowserCloseOutputSchema = {\n ...BrowserBaseOutput,\n tool: z.literal('browser_close'),\n closed: z.boolean(),\n raw: BrowserRawObject.optional(),\n}\n\nexport const BrowserListSessionsOutputSchema = {\n ok: z.boolean(),\n tool: z.literal('browser_list_sessions'),\n session_id: z.null(),\n sessions: z.array(BrowserRawObject),\n count: z.number().int().min(0),\n}\n","import { execFile } from 'node:child_process'\nimport { mkdtemp, rm, stat, writeFile } from 'node:fs/promises'\nimport { tmpdir } from 'node:os'\nimport { join } from 'node:path'\nimport { promisify } from 'node:util'\n\nconst execFileAsync = promisify(execFile)\n\nexport type ReplayAnnotationType = 'box' | 'circle' | 'underline' | 'arrow' | 'label'\n\nexport interface ReplayAnnotation {\n type?: ReplayAnnotationType\n start_seconds?: number\n end_seconds?: number\n left?: number\n top?: number\n width?: number\n height?: number\n x?: number\n y?: number\n from_x?: number\n from_y?: number\n to_x?: number\n to_y?: number\n label?: string\n color?: string\n thickness?: number\n}\n\nexport interface AnnotateReplayOptions {\n annotations: ReplayAnnotation[]\n sourceWidth?: number\n sourceHeight?: number\n sourceLeftOffset?: number\n sourceTopOffset?: number\n}\n\ninterface Size {\n width: number\n height: number\n}\n\ninterface Rect {\n left: number\n top: number\n width: number\n height: number\n}\n\ninterface CoordinateTransform {\n scaleX: number\n scaleY: number\n offsetX: number\n offsetY: number\n}\n\nexport interface AnnotatedReplayResult {\n filePath: string\n bytes: number\n width: number\n height: number\n annotationCount: number\n}\n\nfunction finiteNumber(value: unknown): value is number {\n return typeof value === 'number' && Number.isFinite(value)\n}\n\nfunction clamp(value: number, min: number, max: number): number {\n return Math.min(max, Math.max(min, value))\n}\n\nfunction formatAssTime(seconds: number): string {\n const safe = Math.max(0, seconds)\n const hours = Math.floor(safe / 3600)\n const minutes = Math.floor((safe % 3600) / 60)\n const wholeSeconds = Math.floor(safe % 60)\n const centiseconds = Math.floor((safe - Math.floor(safe)) * 100)\n return `${hours}:${String(minutes).padStart(2, '0')}:${String(wholeSeconds).padStart(2, '0')}.${String(centiseconds).padStart(2, '0')}`\n}\n\nfunction cssHexToAssColor(color?: string): string {\n const normalized = color?.trim().match(/^#?([0-9a-fA-F]{6})$/)?.[1] ?? 'ff3b30'\n const red = normalized.slice(0, 2)\n const green = normalized.slice(2, 4)\n const blue = normalized.slice(4, 6)\n return `&H${blue}${green}${red}&`\n}\n\nfunction escapeAssText(value: string): string {\n return value\n .replace(/\\\\/g, '\\\\\\\\')\n .replace(/\\{/g, '\\\\{')\n .replace(/\\}/g, '\\\\}')\n .replace(/\\r?\\n/g, '\\\\N')\n}\n\nfunction resolveCoordinateTransform(video: Size, source: Size, options: AnnotateReplayOptions): CoordinateTransform {\n const explicitOffsetX = finiteNumber(options.sourceLeftOffset) ? options.sourceLeftOffset : null\n const explicitOffsetY = finiteNumber(options.sourceTopOffset) ? options.sourceTopOffset : null\n const inferredTopOffset =\n explicitOffsetY == null &&\n Math.round(source.width) === Math.round(video.width) &&\n source.height < video.height &&\n video.height - source.height <= 220\n ? video.height - source.height\n : 0\n const offsetX = explicitOffsetX ?? 0\n const offsetY = explicitOffsetY ?? inferredTopOffset\n return {\n scaleX: (video.width - offsetX) / source.width,\n scaleY: (video.height - offsetY) / source.height,\n offsetX,\n offsetY,\n }\n}\n\nfunction transformPoint(x: number, y: number, transform: CoordinateTransform): { x: number; y: number } {\n return {\n x: x * transform.scaleX + transform.offsetX,\n y: y * transform.scaleY + transform.offsetY,\n }\n}\n\nfunction scaleRect(annotation: ReplayAnnotation, transform: CoordinateTransform): Rect {\n if (\n finiteNumber(annotation.left) &&\n finiteNumber(annotation.top) &&\n finiteNumber(annotation.width) &&\n finiteNumber(annotation.height)\n ) {\n const origin = transformPoint(annotation.left, annotation.top, transform)\n return {\n left: origin.x,\n top: origin.y,\n width: annotation.width * transform.scaleX,\n height: annotation.height * transform.scaleY,\n }\n }\n if (finiteNumber(annotation.x) && finiteNumber(annotation.y)) {\n const radius = finiteNumber(annotation.width) ? annotation.width / 2 : 28\n const point = transformPoint(annotation.x, annotation.y, transform)\n return {\n left: point.x - radius * transform.scaleX,\n top: point.y - radius * transform.scaleY,\n width: radius * 2 * transform.scaleX,\n height: radius * 2 * transform.scaleY,\n }\n }\n throw new Error('annotation needs either left/top/width/height or x/y')\n}\n\nfunction rectPath(rect: Rect): string {\n const x1 = Math.round(rect.left)\n const y1 = Math.round(rect.top)\n const x2 = Math.round(rect.left + rect.width)\n const y2 = Math.round(rect.top + rect.height)\n return `m ${x1} ${y1} l ${x2} ${y1} l ${x2} ${y2} l ${x1} ${y2} l ${x1} ${y1}`\n}\n\nfunction ellipsePath(rect: Rect): string {\n const cx = rect.left + rect.width / 2\n const cy = rect.top + rect.height / 2\n const rx = Math.max(4, rect.width / 2)\n const ry = Math.max(4, rect.height / 2)\n const points: string[] = []\n for (let i = 0; i <= 32; i += 1) {\n const t = (Math.PI * 2 * i) / 32\n const x = Math.round(cx + Math.cos(t) * rx)\n const y = Math.round(cy + Math.sin(t) * ry)\n points.push(`${i === 0 ? 'm' : 'l'} ${x} ${y}`)\n }\n return points.join(' ')\n}\n\nfunction filledRectPath(left: number, top: number, width: number, height: number): string {\n return rectPath({ left, top, width, height })\n}\n\nfunction arrowPath(fromX: number, fromY: number, toX: number, toY: number, thickness: number): string {\n const dx = toX - fromX\n const dy = toY - fromY\n const length = Math.max(1, Math.hypot(dx, dy))\n const ux = dx / length\n const uy = dy / length\n const px = -uy\n const py = ux\n const half = thickness / 2\n const head = Math.max(14, thickness * 4)\n const baseX = toX - ux * head\n const baseY = toY - uy * head\n const p1x = Math.round(fromX + px * half)\n const p1y = Math.round(fromY + py * half)\n const p2x = Math.round(baseX + px * half)\n const p2y = Math.round(baseY + py * half)\n const p3x = Math.round(baseX + px * head * 0.55)\n const p3y = Math.round(baseY + py * head * 0.55)\n const p4x = Math.round(toX)\n const p4y = Math.round(toY)\n const p5x = Math.round(baseX - px * head * 0.55)\n const p5y = Math.round(baseY - py * head * 0.55)\n const p6x = Math.round(baseX - px * half)\n const p6y = Math.round(baseY - py * half)\n const p7x = Math.round(fromX - px * half)\n const p7y = Math.round(fromY - py * half)\n return `m ${p1x} ${p1y} l ${p2x} ${p2y} l ${p3x} ${p3y} l ${p4x} ${p4y} l ${p5x} ${p5y} l ${p6x} ${p6y} l ${p7x} ${p7y} l ${p1x} ${p1y}`\n}\n\nfunction eventLine(start: number, end: number, body: string): string {\n return `Dialogue: 0,${formatAssTime(start)},${formatAssTime(end)},Default,,0,0,0,,${body}`\n}\n\nfunction labelLine(start: number, end: number, x: number, y: number, label: string): string {\n const safeX = Math.round(x)\n const safeY = Math.round(y)\n return eventLine(\n start,\n end,\n `{\\\\an7\\\\pos(${safeX},${safeY})\\\\fs30\\\\bord4\\\\shad0\\\\c&HFFFFFF&\\\\3c&H000000&}${escapeAssText(label)}`,\n )\n}\n\nfunction shapeLine(start: number, end: number, path: string, color: string, thickness: number, filled = false): string {\n const fillAlpha = filled ? '&H00&' : '&HFF&'\n const border = filled ? 0 : thickness\n return eventLine(\n start,\n end,\n `{\\\\an7\\\\pos(0,0)\\\\p1\\\\bord${border}\\\\shad0\\\\1a${fillAlpha}\\\\1c${color}\\\\3c${color}}${path}`,\n )\n}\n\nexport function buildAssSubtitle(options: AnnotateReplayOptions, video: Size): string {\n const source = {\n width: options.sourceWidth && options.sourceWidth > 0 ? options.sourceWidth : video.width,\n height: options.sourceHeight && options.sourceHeight > 0 ? options.sourceHeight : video.height,\n }\n const transform = resolveCoordinateTransform(video, source, options)\n const lines: string[] = []\n for (const annotation of options.annotations) {\n const start = Math.max(0, annotation.start_seconds ?? 0)\n const end = annotation.end_seconds && annotation.end_seconds > start ? annotation.end_seconds : start + 2\n const type = annotation.type ?? 'box'\n const color = cssHexToAssColor(annotation.color)\n const thickness = Math.round(clamp(annotation.thickness ?? 5, 2, 24))\n if (type === 'label') {\n if (!annotation.label) throw new Error('label annotation needs label')\n const rect = scaleRect(annotation, transform)\n lines.push(labelLine(start, end, rect.left, Math.max(8, rect.top), annotation.label))\n continue\n }\n if (type === 'arrow') {\n const rect = finiteNumber(annotation.to_x) && finiteNumber(annotation.to_y)\n ? null\n : scaleRect(annotation, transform)\n const toPoint = finiteNumber(annotation.to_x) && finiteNumber(annotation.to_y)\n ? transformPoint(annotation.to_x, annotation.to_y, transform)\n : null\n const toX = toPoint ? toPoint.x : rect!.left + rect!.width / 2\n const toY = toPoint ? toPoint.y : rect!.top + rect!.height / 2\n const fromPoint = finiteNumber(annotation.from_x) && finiteNumber(annotation.from_y)\n ? transformPoint(annotation.from_x, annotation.from_y, transform)\n : null\n const fromX = fromPoint ? fromPoint.x : clamp(toX - 120, 12, video.width - 12)\n const fromY = fromPoint ? fromPoint.y : clamp(toY - 90, 12, video.height - 12)\n lines.push(shapeLine(start, end, arrowPath(fromX, fromY, toX, toY, thickness), color, thickness, true))\n if (annotation.label) lines.push(labelLine(start, end, fromX + 8, Math.max(96, fromY - 36), annotation.label))\n continue\n }\n const rect = scaleRect(annotation, transform)\n if (type === 'underline') {\n const underlineTop = rect.top + rect.height + thickness\n lines.push(shapeLine(start, end, filledRectPath(rect.left, underlineTop, rect.width, thickness), color, 0, true))\n } else if (type === 'circle') {\n lines.push(shapeLine(start, end, ellipsePath(rect), color, thickness))\n } else {\n lines.push(shapeLine(start, end, rectPath(rect), color, thickness))\n }\n if (annotation.label) lines.push(labelLine(start, end, rect.left, Math.max(8, rect.top - 38), annotation.label))\n }\n\n return [\n '[Script Info]',\n 'ScriptType: v4.00+',\n `PlayResX: ${Math.round(video.width)}`,\n `PlayResY: ${Math.round(video.height)}`,\n 'ScaledBorderAndShadow: yes',\n '',\n '[V4+ Styles]',\n 'Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding',\n 'Style: Default,Arial,30,&H00FFFFFF,&H000000FF,&H00000000,&H90000000,0,0,0,0,100,100,0,0,1,2,0,7,0,0,0,1',\n '',\n '[Events]',\n 'Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text',\n ...lines,\n '',\n ].join('\\n')\n}\n\nasync function videoSize(inputFilePath: string): Promise<Size> {\n const { stdout } = await execFileAsync('ffprobe', [\n '-v',\n 'error',\n '-select_streams',\n 'v:0',\n '-show_entries',\n 'stream=width,height',\n '-of',\n 'json',\n inputFilePath,\n ], { maxBuffer: 1024 * 1024 })\n const parsed = JSON.parse(stdout) as { streams?: Array<{ width?: number; height?: number }> }\n const stream = parsed.streams?.[0]\n if (!stream?.width || !stream.height) throw new Error('could not read replay video dimensions')\n return { width: stream.width, height: stream.height }\n}\n\nfunction ffmpegFilterPath(path: string): string {\n return path.replace(/\\\\/g, '\\\\\\\\').replace(/:/g, '\\\\:')\n}\n\nexport async function annotateReplayVideo(\n inputFilePath: string,\n outputFilePath: string,\n options: AnnotateReplayOptions,\n): Promise<AnnotatedReplayResult> {\n if (!options.annotations.length) throw new Error('annotations must include at least one item')\n const size = await videoSize(inputFilePath)\n const tmp = await mkdtemp(join(tmpdir(), 'mcp-scraper-ass-'))\n const assPath = join(tmp, 'annotations.ass')\n try {\n await writeFile(assPath, buildAssSubtitle(options, size), 'utf8')\n await execFileAsync('ffmpeg', [\n '-y',\n '-i',\n inputFilePath,\n '-vf',\n `ass=${ffmpegFilterPath(assPath)}`,\n '-c:v',\n 'libx264',\n '-pix_fmt',\n 'yuv420p',\n '-movflags',\n '+faststart',\n '-c:a',\n 'copy',\n outputFilePath,\n ], { maxBuffer: 1024 * 1024 * 20 })\n const out = await stat(outputFilePath)\n return {\n filePath: outputFilePath,\n bytes: out.size,\n width: size.width,\n height: size.height,\n annotationCount: options.annotations.length,\n }\n } finally {\n await rm(tmp, { recursive: true, force: true })\n }\n}\n"],"mappings":"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","names":["join","rect","join","finiteNumber"]}