mcp-scraper 0.2.6 → 0.2.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -6
- package/dist/bin/api-server.cjs +582 -171
- package/dist/bin/api-server.cjs.map +1 -1
- package/dist/bin/api-server.js +2 -2
- package/dist/bin/browser-agent-stdio-server.cjs +442 -9
- package/dist/bin/browser-agent-stdio-server.cjs.map +1 -1
- package/dist/bin/browser-agent-stdio-server.js +2 -2
- package/dist/bin/mcp-scraper-combined-stdio-server.cjs +519 -31
- package/dist/bin/mcp-scraper-combined-stdio-server.cjs.map +1 -1
- package/dist/bin/mcp-scraper-combined-stdio-server.js +3 -3
- package/dist/bin/mcp-scraper-install.cjs +2 -1
- package/dist/bin/mcp-scraper-install.cjs.map +1 -1
- package/dist/bin/mcp-scraper-install.js +2 -1
- package/dist/bin/mcp-scraper-install.js.map +1 -1
- package/dist/bin/mcp-stdio-server.cjs +68 -13
- package/dist/bin/mcp-stdio-server.cjs.map +1 -1
- package/dist/bin/mcp-stdio-server.js +2 -2
- package/dist/bin/paa-harvest.cjs +42 -6
- package/dist/bin/paa-harvest.cjs.map +1 -1
- package/dist/bin/paa-harvest.js +1 -1
- package/dist/chunk-5HMOPP76.js +793 -0
- package/dist/chunk-5HMOPP76.js.map +1 -0
- package/dist/{chunk-7WB2W6FO.js → chunk-6NEXSNSA.js} +69 -14
- package/dist/chunk-6NEXSNSA.js.map +1 -0
- package/dist/{chunk-MY3S7EX7.js → chunk-CQTAKXBN.js} +43 -7
- package/dist/chunk-CQTAKXBN.js.map +1 -0
- package/dist/chunk-I26QN7WQ.js +7 -0
- package/dist/chunk-I26QN7WQ.js.map +1 -0
- package/dist/index.cjs +42 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1 -1
- package/dist/{server-MKCU3M7Y.js → server-BTTDFPSQ.js} +456 -142
- package/dist/server-BTTDFPSQ.js.map +1 -0
- package/dist/{worker-NAKGTIF5.js → worker-5O44YBF4.js} +2 -2
- package/docs/mcp-tool-quality-spec.md +3 -1
- package/package.json +1 -1
- package/dist/chunk-7WB2W6FO.js.map +0 -1
- package/dist/chunk-MY3S7EX7.js.map +0 -1
- package/dist/chunk-T6SIPPOQ.js +0 -7
- package/dist/chunk-T6SIPPOQ.js.map +0 -1
- package/dist/chunk-XL4V7VKD.js +0 -360
- package/dist/chunk-XL4V7VKD.js.map +0 -1
- package/dist/server-MKCU3M7Y.js.map +0 -1
- /package/dist/{worker-NAKGTIF5.js.map → worker-5O44YBF4.js.map} +0 -0
|
@@ -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 { PACKAGE_VERSION } from '../version.js'\nimport {\n BrowserOpenInputSchema,\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} 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 textResult(value: unknown, isError = false): CallToolResult {\n return { content: [{ type: 'text', text: JSON.stringify(value) }], isError }\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 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_open',\n {\n title: 'Open Browser Session',\n description:\n 'Open a fresh cloud browser you can drive. Returns a session_id used by all other browser_* tools, and a watch_url where a human can watch live or take over. Anti-bot stealth and automatic CAPTCHA/Cloudflare solving are on by default: if a Cloudflare or CAPTCHA challenge appears, do NOT click it — wait a few seconds and call browser_screenshot again; it is solved automatically. Billing: metered per second of active browser work at ~4 credits per minute; idle and standby time are free. Call browser_close when done to stop the meter. After opening, call browser_screenshot to see the page.',\n inputSchema: BrowserOpenInputSchema,\n annotations: annotations('Open Browser Session'),\n },\n async input => {\n const open = await req('POST', '/agent/sessions', {\n label: input.label,\n profile: input.profile,\n timeout_seconds: input.timeout_seconds,\n })\n if (!open.ok) return textResult(open.data, true)\n const session = open.data\n if (input.url) {\n await req('POST', `/agent/sessions/${session.session_id}/goto`, { url: input.url })\n }\n return textResult({\n session_id: session.session_id,\n watch_url: `${consoleBase}/console/${session.session_id}`,\n live_view_url: session.live_view_url ?? null,\n hint: 'Call browser_screenshot to see the page. Click by the x,y of an element from the snapshot.',\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 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 textResult(res.data, true)\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 content.push({\n type: 'text',\n text: JSON.stringify({ url, title, elements, text }),\n })\n return { content }\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 annotations: annotations('Read Page', true),\n },\n async input => {\n const res = await req('POST', `/agent/sessions/${input.session_id}/read`)\n return textResult(res.data, !res.ok)\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 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 return textResult(res.data, !res.ok)\n },\n )\n\n server.registerTool(\n 'browser_goto',\n {\n title: 'Navigate To URL',\n description: 'Navigate the browser to a URL. Follow with browser_screenshot to see the result.',\n inputSchema: BrowserGotoInputSchema,\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 textResult(res.data, !res.ok)\n },\n )\n\n server.registerTool(\n 'browser_click',\n {\n title: 'Click',\n description: 'Click at x,y (screenshot pixel coordinates). Use the x,y of a target element from the latest browser_screenshot or browser_read.',\n inputSchema: BrowserClickInputSchema,\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 textResult(res.data, !res.ok)\n },\n )\n\n server.registerTool(\n 'browser_type',\n {\n title: 'Type Text',\n description: 'Type text at the current focus. Click an input field first to focus it. Use browser_press with [\"Return\"] to submit.',\n inputSchema: BrowserTypeInputSchema,\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 textResult(res.data, !res.ok)\n },\n )\n\n server.registerTool(\n 'browser_scroll',\n {\n title: 'Scroll',\n description: 'Scroll the page. Positive delta_y scrolls down. Follow with browser_screenshot to see newly revealed content.',\n inputSchema: BrowserScrollInputSchema,\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 textResult(res.data, !res.ok)\n },\n )\n\n server.registerTool(\n 'browser_press',\n {\n title: 'Press Keys',\n description: 'Press keys or combinations, e.g. [\"Return\"] to submit, [\"Ctrl+a\"] to select all, [\"Ctrl+Shift+Tab\"] to switch tabs.',\n inputSchema: BrowserPressInputSchema,\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 textResult(res.data, !res.ok)\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 annotations: annotations('Start Recording'),\n },\n async input => {\n const res = await req('POST', `/agent/sessions/${input.session_id}/replay/start`)\n return textResult(res.data, !res.ok)\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 locally.',\n inputSchema: BrowserReplayStopInputSchema,\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 return textResult(res.data, !res.ok)\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 annotations: annotations('List Replay Videos', true),\n },\n async input => {\n const res = await req('GET', `/agent/sessions/${input.session_id}/replays`)\n return textResult(res.data, !res.ok)\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 locally under MCP_SCRAPER_OUTPUT_DIR/browser-replays. Use after browser_replay_stop or browser_list_replays.',\n inputSchema: BrowserReplayDownloadInputSchema,\n annotations: annotations('Download Replay MP4', true),\n },\n async input => {\n const res = await downloadReplay(input.session_id, input.replay_id, input.filename)\n return textResult(res.data, !res.ok)\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 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 textResult(res.data, true)\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 textResult({ error: target?.error ?? 'target not found in current viewport', target }, true)\n }\n if (!finiteNumber(elapsed)) {\n return textResult({ error: 'no active replay clock found; call browser_replay_start before browser_replay_mark' }, true)\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 textResult({\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 locally. 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 annotations: annotations('Annotate Replay MP4', true),\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 textResult(downloaded.data, true)\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 textResult({\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 textResult({ error: err instanceof Error ? err.message : String(err) }, true)\n }\n },\n )\n\n server.registerTool(\n 'browser_close',\n {\n title: 'Close Browser Session',\n description: 'Close and release the browser session when the task is done.',\n inputSchema: BrowserSessionInputSchema,\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 return textResult(res.data, !res.ok)\n },\n )\n\n server.registerTool(\n 'browser_list_sessions',\n {\n title: 'List Browser Sessions',\n description: 'List your browser sessions and their status, with a watch_url for each.',\n inputSchema: BrowserListInputSchema,\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 textResult(res.data, true)\n const sessions = (res.data.sessions ?? []).map((s: any) => ({ ...s, watch_url: `${consoleBase}/console/${s.session_id}` }))\n return textResult({ sessions })\n },\n )\n\n}\n","import { z } from 'zod'\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 profile name to load a logged-in session for a site.'),\n timeout_seconds: z.number().int().min(60).max(259200).optional().describe('How long the 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 BrowserSessionInputSchema = {\n session_id: z.string().describe('The session id returned by browser_open.'),\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.'),\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.'),\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.'),\n x: z.number().describe('X coordinate to click, in screenshot pixels. Use the x of an element from the latest screenshot.'),\n y: z.number().describe('Y coordinate to click, in screenshot pixels.'),\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.'),\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.'),\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.'),\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.'),\n replay_id: z.string().describe('The replay id returned by browser_replay_start.'),\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.'),\n replay_id: z.string().describe('The replay id returned by browser_replay_start or browser_list_replays.'),\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. 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.'),\n replay_id: z.string().describe('The replay id returned by browser_replay_start or browser_list_replays.'),\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","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"]}
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
} from "./chunk-M2S27J6Z.js";
|
|
4
4
|
import {
|
|
5
5
|
PACKAGE_VERSION
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-I26QN7WQ.js";
|
|
7
7
|
|
|
8
8
|
// src/harvest-timeout.ts
|
|
9
9
|
var VERCEL_FUNCTION_MAX_MS = 3e5;
|
|
@@ -158,12 +158,17 @@ function errorAttemptsSection(body) {
|
|
|
158
158
|
const browser = debug.browser ?? {};
|
|
159
159
|
const kernel = browser.browserRuntime ?? browser.kernel ?? {};
|
|
160
160
|
const proxyResolution = kernel.proxyResolution ?? {};
|
|
161
|
-
const network = browser.networkLocation ?? {
|
|
161
|
+
const network = browser.networkLocation ?? {
|
|
162
|
+
ip: attempt.observedIp ?? attempt.observed_ip,
|
|
163
|
+
city: attempt.observedCity ?? attempt.observed_city,
|
|
164
|
+
region: attempt.observedRegion ?? attempt.observed_region
|
|
165
|
+
};
|
|
162
166
|
const nav = browser.serpNavigation ?? {};
|
|
163
167
|
const geo = [network.ip, network.city, network.region].filter(Boolean).join(" / ") || "geo unknown";
|
|
164
|
-
const sessionId = attempt.browser_session_id ?? attempt.kernel_session_id ?? kernel.sessionId ?? "unknown";
|
|
168
|
+
const sessionId = attempt.browser_session_id ?? attempt.browserSessionIdSuffix ?? attempt.kernel_session_id ?? kernel.sessionId ?? "unknown";
|
|
165
169
|
const cleanupSucceeded = attempt.session_cleanup_succeeded ?? attempt.kernel_delete_succeeded;
|
|
166
|
-
|
|
170
|
+
const proxySource = proxyResolution.source ?? attempt.proxyResolutionSource;
|
|
171
|
+
return `- Attempt ${attempt.attempt_number ?? attempt.attemptNumber ?? "?"}: ${attempt.outcome ?? attempt.status ?? "unknown"} \xB7 session ${sessionId} \xB7 proxy ${debug.request?.proxyMode ?? kernel.proxyMode ?? attempt.proxyMode ?? "unknown"}${proxySource ? `/${proxySource}` : ""} \xB7 ${geo} \xB7 CAPTCHA ${nav.captchaDetected === true ? "yes" : nav.captchaDetected === false ? "no" : "unknown"} \xB7 cleanup ${cleanupSucceeded === true ? "yes" : cleanupSucceeded === false ? "no" : "unknown"}`;
|
|
167
172
|
});
|
|
168
173
|
return `
|
|
169
174
|
|
|
@@ -624,6 +629,29 @@ ${rows}`,
|
|
|
624
629
|
}
|
|
625
630
|
};
|
|
626
631
|
}
|
|
632
|
+
function normalizeMapsAttempts(value) {
|
|
633
|
+
const attempts = Array.isArray(value) ? value : [];
|
|
634
|
+
return attempts.map((attempt, index) => ({
|
|
635
|
+
attemptNumber: attempt.attemptNumber ?? attempt.attempt_number ?? index + 1,
|
|
636
|
+
maxAttempts: attempt.maxAttempts ?? attempt.max_attempts ?? attempts.length,
|
|
637
|
+
status: attempt.status === "ok" ? "ok" : "failed",
|
|
638
|
+
outcome: attempt.outcome ?? attempt.status ?? "unknown",
|
|
639
|
+
willRetry: attempt.willRetry ?? attempt.will_retry ?? false,
|
|
640
|
+
durationMs: attempt.durationMs ?? attempt.duration_ms ?? 0,
|
|
641
|
+
resultCount: attempt.resultCount ?? attempt.result_count ?? 0,
|
|
642
|
+
error: attempt.error ? sanitizeVendorText(attempt.error) : null,
|
|
643
|
+
proxyMode: attempt.proxyMode ?? attempt.proxy_mode ?? "location",
|
|
644
|
+
proxyResolutionSource: attempt.proxyResolutionSource ?? attempt.proxy_resolution_source ?? null,
|
|
645
|
+
proxyIdSuffix: attempt.proxyIdSuffix ?? attempt.proxy_id_suffix ?? null,
|
|
646
|
+
proxyTargetLevel: attempt.proxyTargetLevel ?? attempt.proxy_target_level ?? null,
|
|
647
|
+
proxyTargetLocation: attempt.proxyTargetLocation ?? attempt.proxy_target_location ?? null,
|
|
648
|
+
proxyTargetZip: attempt.proxyTargetZip ?? attempt.proxy_target_zip ?? null,
|
|
649
|
+
browserSessionIdSuffix: attempt.browserSessionIdSuffix ?? attempt.browser_session_id ?? null,
|
|
650
|
+
observedIp: attempt.observedIp ?? attempt.observed_ip ?? null,
|
|
651
|
+
observedCity: attempt.observedCity ?? attempt.observed_city ?? null,
|
|
652
|
+
observedRegion: attempt.observedRegion ?? attempt.observed_region ?? null
|
|
653
|
+
}));
|
|
654
|
+
}
|
|
627
655
|
function formatCreditsInfo(raw, input) {
|
|
628
656
|
const parsed = parseData(raw);
|
|
629
657
|
if ("error" in parsed) return { content: [{ type: "text", text: parsed.error }], isError: true };
|
|
@@ -696,6 +724,8 @@ function formatMapsSearch(raw, input) {
|
|
|
696
724
|
const searchQuery = d.searchQuery ?? [input.query, input.location].filter(Boolean).join(" ");
|
|
697
725
|
const requestedMax = d.requestedMaxResults ?? input.maxResults ?? 10;
|
|
698
726
|
const durationMs = d.durationMs;
|
|
727
|
+
const attempts = normalizeMapsAttempts(d.attempts);
|
|
728
|
+
const lastAttempt = attempts.at(-1);
|
|
699
729
|
const rows = results.map((r) => {
|
|
700
730
|
const rating = [r.rating, r.reviewCount ? `(${r.reviewCount})` : null].filter(Boolean).join(" ");
|
|
701
731
|
return `| ${r.position} | ${cell(r.name)} | ${cell(r.category)} | ${cell(rating)} | ${cell(r.address)} | ${r.cidDecimal ? `\`${r.cidDecimal}\`` : "\u2014"} | ${r.websiteUrl ? `[site](${r.websiteUrl})` : "\u2014"} | [maps](${r.placeUrl}) |`;
|
|
@@ -710,6 +740,7 @@ ${meta}`;
|
|
|
710
740
|
const full = [
|
|
711
741
|
`# Google Maps Search: "${searchQuery}"`,
|
|
712
742
|
`**Returned:** ${results.length} profile candidate${results.length === 1 ? "" : "s"} \xB7 **Requested max:** ${requestedMax} \xB7 **Limit:** 50`,
|
|
743
|
+
attempts.length ? `**Attempts:** ${attempts.length}/${lastAttempt?.maxAttempts ?? attempts.length} \xB7 **Proxy:** ${lastAttempt?.proxyMode ?? "unknown"}${lastAttempt?.proxyResolutionSource ? `/${lastAttempt.proxyResolutionSource}` : ""} \xB7 **Observed:** ${[lastAttempt?.observedCity, lastAttempt?.observedRegion].filter(Boolean).join(", ") || "unknown"}` : null,
|
|
713
744
|
`
|
|
714
745
|
## Results
|
|
715
746
|
| # | Name | Category | Rating | Address | CID | Website | Maps |
|
|
@@ -733,6 +764,7 @@ ${rows}`,
|
|
|
733
764
|
requestedMaxResults: requestedMax,
|
|
734
765
|
resultCount: results.length,
|
|
735
766
|
results: normalizedResults,
|
|
767
|
+
attempts,
|
|
736
768
|
durationMs: durationMs ?? 0
|
|
737
769
|
}
|
|
738
770
|
};
|
|
@@ -743,6 +775,7 @@ function formatDirectoryWorkflow(raw, input) {
|
|
|
743
775
|
const d = parsed.data;
|
|
744
776
|
const cities = (d.cities ?? []).map((city) => ({
|
|
745
777
|
...city,
|
|
778
|
+
attempts: normalizeMapsAttempts(city.attempts),
|
|
746
779
|
results: city.results.map((result) => ({
|
|
747
780
|
...result,
|
|
748
781
|
phone: result.phone ?? null,
|
|
@@ -970,7 +1003,7 @@ var HarvestPaaInputSchema = {
|
|
|
970
1003
|
gl: z.string().length(2).default("us").describe("Google country code inferred from location or user language. Examples: United States us, United Kingdom gb, Japan jp, Canada ca, Australia au."),
|
|
971
1004
|
hl: z.string().default("en").describe("Google interface/content language inferred from the user request. Use en unless the user asks for another language or locale."),
|
|
972
1005
|
device: z.enum(["desktop", "mobile"]).default("desktop").describe("SERP device context. Use desktop by default; use mobile only when the user asks for mobile rankings."),
|
|
973
|
-
proxyMode: z.enum(["location", "configured", "none"]).default("location").describe("Proxy targeting mode. Use location by default for US city/state SERPs; it creates a fresh residential proxy ID per attempt
|
|
1006
|
+
proxyMode: z.enum(["location", "configured", "none"]).default("location").describe("Proxy targeting mode. Use location by default for US city/state SERPs; it creates a fresh residential proxy ID per attempt. If Google shows a CAPTCHA/challenge, browser-service sessions briefly wait for the automatic solver first, then retry with a fresh proxy/session if the challenge does not clear. Also retries proxy tunnel failure and wrong-location evidence before returning. Use configured only for the static configured proxy. Use none only for direct-network debugging."),
|
|
974
1007
|
proxyZip: z.string().regex(/^\d{5}$/).optional().describe("Optional US ZIP override for residential location proxy targeting. Use when the user gives a specific ZIP or when city-center targeting needs to be forced. With proxyMode location this ZIP is used for each fresh proxy attempt."),
|
|
975
1008
|
debug: z.boolean().default(false).describe("Include sanitized browser/session/location diagnostics in the response. Use true when debugging localization, CAPTCHA, or proxy behavior.")
|
|
976
1009
|
};
|
|
@@ -1029,7 +1062,7 @@ var MapsSearchInputSchema = {
|
|
|
1029
1062
|
gl: z.string().length(2).default("us").describe("Google country code inferred from location."),
|
|
1030
1063
|
hl: z.string().length(2).default("en").describe("Language inferred from user request."),
|
|
1031
1064
|
maxResults: z.number().int().min(1).max(50).default(10).describe("Number of Google Maps business/profile candidates to return. Default 10. Maximum 50. Use 10 unless the user asks for more."),
|
|
1032
|
-
proxyMode: z.enum(["location", "configured", "none"]).default("location").describe("Proxy targeting mode. Use location by default for US city/state Maps searches;
|
|
1065
|
+
proxyMode: z.enum(["location", "configured", "none"]).default("location").describe("Proxy targeting mode. Use location by default for US city/state Maps searches; retryable failures create a new residential proxy ID and new browser session for up to 5 attempts. Use configured for the server proxy ID, and none only for local direct-network debugging."),
|
|
1033
1066
|
proxyZip: z.string().regex(/^\d{5}$/).optional().describe("Optional US ZIP override for residential location proxy targeting. Use when the user gives a specific ZIP or city-center ZIP."),
|
|
1034
1067
|
debug: z.boolean().default(false).describe("Include sanitized browser/proxy diagnostics when debugging Maps localization, CAPTCHA, or proxy behavior.")
|
|
1035
1068
|
};
|
|
@@ -1044,7 +1077,7 @@ var DirectoryWorkflowInputSchema = {
|
|
|
1044
1077
|
includeZipGroups: z.boolean().default(true).describe("Attach ZIP groups from a configured US ZIPS CSV when available. Set MCP_SCRAPER_USZIPS_CSV_PATH on the API server or pass usZipsCsvPath in local/test mode."),
|
|
1045
1078
|
usZipsCsvPath: z.string().optional().describe("Local/test-only path to a US ZIPS CSV with state_abbr, zipcode, county, city columns, such as Lead Magician tools/analytics/data/uszips.csv. Deployed APIs should use MCP_SCRAPER_USZIPS_CSV_PATH instead."),
|
|
1046
1079
|
saveCsv: z.boolean().default(true).describe("Save a directory-ready CSV to the MCP Scraper output directory and return its path. CSV rows include source_location, result_position, business_name, review_stars, category, address, phone, hours_status, website_url, directions_url, place_url, CID fields, population, and ZIP groups."),
|
|
1047
|
-
proxyMode: z.enum(["location", "configured", "none"]).default("location").describe("Proxy targeting mode for every city Maps search. Use location by default for US city/state batches;
|
|
1080
|
+
proxyMode: z.enum(["location", "configured", "none"]).default("location").describe("Proxy targeting mode for every city Maps search. Use location by default for US city/state batches; retryable failures create a new residential proxy ID and new browser session for up to 5 attempts per city. Use configured for the server proxy ID, and none only for local direct-network debugging."),
|
|
1048
1081
|
proxyZip: z.string().regex(/^\d{5}$/).optional().describe("Optional ZIP override for proxy targeting. Normally omit it so each city can use its Lead Magician ZIP group or city/state location."),
|
|
1049
1082
|
debug: z.boolean().default(false).describe("Include sanitized browser/proxy diagnostics in each Maps browser session when supported.")
|
|
1050
1083
|
};
|
|
@@ -1067,6 +1100,26 @@ var RankTrackerBlueprintInputSchema = {
|
|
|
1067
1100
|
notes: z.string().max(4e3).optional().describe("Extra product, client, stack, or hosting requirements to include in the implementation prompt.")
|
|
1068
1101
|
};
|
|
1069
1102
|
var NullableString = z.string().nullable();
|
|
1103
|
+
var MapsSearchAttemptOutput = z.object({
|
|
1104
|
+
attemptNumber: z.number().int().min(1),
|
|
1105
|
+
maxAttempts: z.number().int().min(1),
|
|
1106
|
+
status: z.enum(["ok", "failed"]),
|
|
1107
|
+
outcome: z.string(),
|
|
1108
|
+
willRetry: z.boolean(),
|
|
1109
|
+
durationMs: z.number().int().min(0),
|
|
1110
|
+
resultCount: z.number().int().min(0),
|
|
1111
|
+
error: NullableString,
|
|
1112
|
+
proxyMode: z.enum(["location", "configured", "none"]),
|
|
1113
|
+
proxyResolutionSource: z.enum(["disabled", "location_reused", "location_created", "configured_fallback", "unavailable"]).nullable(),
|
|
1114
|
+
proxyIdSuffix: NullableString,
|
|
1115
|
+
proxyTargetLevel: z.enum(["zip", "city", "state"]).nullable(),
|
|
1116
|
+
proxyTargetLocation: NullableString,
|
|
1117
|
+
proxyTargetZip: NullableString,
|
|
1118
|
+
browserSessionIdSuffix: NullableString,
|
|
1119
|
+
observedIp: NullableString,
|
|
1120
|
+
observedCity: NullableString,
|
|
1121
|
+
observedRegion: NullableString
|
|
1122
|
+
});
|
|
1070
1123
|
var MapsSearchOutputSchema = {
|
|
1071
1124
|
query: z.string(),
|
|
1072
1125
|
location: z.string().nullable(),
|
|
@@ -1091,6 +1144,7 @@ var MapsSearchOutputSchema = {
|
|
|
1091
1144
|
directionsUrl: NullableString,
|
|
1092
1145
|
metadata: z.array(z.string())
|
|
1093
1146
|
})),
|
|
1147
|
+
attempts: z.array(MapsSearchAttemptOutput),
|
|
1094
1148
|
durationMs: z.number().int().min(0)
|
|
1095
1149
|
};
|
|
1096
1150
|
var DirectoryMapsBusinessOutput = z.object({
|
|
@@ -1137,6 +1191,7 @@ var DirectoryWorkflowOutputSchema = {
|
|
|
1137
1191
|
error: NullableString,
|
|
1138
1192
|
resultCount: z.number().int().min(0),
|
|
1139
1193
|
durationMs: z.number().int().min(0),
|
|
1194
|
+
attempts: z.array(MapsSearchAttemptOutput),
|
|
1140
1195
|
results: z.array(DirectoryMapsBusinessOutput)
|
|
1141
1196
|
})),
|
|
1142
1197
|
durationMs: z.number().int().min(0)
|
|
@@ -1353,7 +1408,7 @@ var SearchSerpInputSchema = {
|
|
|
1353
1408
|
gl: z.string().length(2).default("us").describe("Google country code inferred from location or user language."),
|
|
1354
1409
|
hl: z.string().default("en").describe("Google interface/content language inferred from user request."),
|
|
1355
1410
|
device: z.enum(["desktop", "mobile"]).default("desktop").describe("SERP device context. Use desktop by default; use mobile only when the user asks for mobile rankings."),
|
|
1356
|
-
proxyMode: z.enum(["location", "configured", "none"]).default("location").describe("Proxy targeting mode. Use location by default for US city/state SERPs; it creates a fresh residential proxy ID per attempt
|
|
1411
|
+
proxyMode: z.enum(["location", "configured", "none"]).default("location").describe("Proxy targeting mode. Use location by default for US city/state SERPs; it creates a fresh residential proxy ID per attempt. If Google shows a CAPTCHA/challenge, browser-service sessions briefly wait for the automatic solver first, then retry with a fresh proxy/session if the challenge does not clear. Also retries proxy tunnel failure and wrong-location evidence before returning. Use configured only for the static configured proxy. Use none only for direct-network debugging."),
|
|
1357
1412
|
proxyZip: z.string().regex(/^\d{5}$/).optional().describe("Optional US ZIP override for residential location proxy targeting. Use when the user gives a specific ZIP or when city-center targeting needs to be forced. With proxyMode location this ZIP is used for each fresh proxy attempt."),
|
|
1358
1413
|
debug: z.boolean().default(false).describe("Include sanitized browser/session/location diagnostics in the response. Use true when debugging localization, CAPTCHA, or proxy behavior."),
|
|
1359
1414
|
pages: z.number().int().min(1).max(2).default(1).describe("Number of result pages to fetch (1\u20132)")
|
|
@@ -1364,7 +1419,7 @@ var CaptureSerpSnapshotInputSchema = {
|
|
|
1364
1419
|
gl: z.string().length(2).default("us").describe("Google country code inferred from the requested market, e.g. us, gb, ca, au."),
|
|
1365
1420
|
hl: z.string().default("en").describe("Google interface/content language inferred from the user request."),
|
|
1366
1421
|
device: z.enum(["desktop", "mobile"]).default("desktop").describe("SERP device context. Use mobile only when the user asks for mobile rankings or mobile SERP evidence."),
|
|
1367
|
-
proxyMode: z.enum(["location", "configured", "none"]).default("location").describe("Proxy behavior for capture. Use location for localized US residential evidence; it creates a fresh proxy ID per attempt
|
|
1422
|
+
proxyMode: z.enum(["location", "configured", "none"]).default("location").describe("Proxy behavior for capture. Use location for localized US residential evidence; it creates a fresh proxy ID per attempt. If Google shows a CAPTCHA/challenge, browser-service sessions briefly wait for the automatic solver first, then retry with a fresh proxy/session if the challenge does not clear. Also retries proxy tunnel failure and wrong-location evidence before returning. Use configured only for the static residential proxy, and none only for direct-network debugging."),
|
|
1368
1423
|
proxyZip: z.string().regex(/^\d{5}$/).optional().describe("Optional US ZIP override for residential location proxy targeting when a precise city-center or ZIP proxy is needed. With proxyMode location this ZIP is used for each fresh proxy attempt."),
|
|
1369
1424
|
pages: z.number().int().min(1).max(2).default(1).describe("Number of Google result pages to capture. Use 1 normally and 2 only when the user needs deeper ranking evidence."),
|
|
1370
1425
|
debug: z.boolean().default(false).describe("Include sanitized browser, proxy, and location diagnostics. Use true when debugging localization, CAPTCHA, proxy selection, or capture reliability."),
|
|
@@ -1776,14 +1831,14 @@ function registerPaaExtractorMcpTools(server, executor, options = {}) {
|
|
|
1776
1831
|
if (savesReports) registerSavedReportResources(server);
|
|
1777
1832
|
server.registerTool("harvest_paa", {
|
|
1778
1833
|
title: "Google PAA + SERP Harvest",
|
|
1779
|
-
description: withReportNote('Best default tool for Google search research. Extracts People Also Ask questions plus answers/source URLs, organic SERP, local pack when present, entity IDs (CID/GCID/KG MID), and AI Overview. Infer the user language: split topic from location (e.g. "best hvac company in Denver CO" => query "best hvac company", location "Denver, CO", gl "us", hl "en"). For US local SERPs, leave proxyMode as location so the service uses fresh residential proxy IDs across retries
|
|
1834
|
+
description: withReportNote('Best default tool for Google search research. Extracts People Also Ask questions plus answers/source URLs, organic SERP, local pack when present, entity IDs (CID/GCID/KG MID), and AI Overview. Infer the user language: split topic from location (e.g. "best hvac company in Denver CO" => query "best hvac company", location "Denver, CO", gl "us", hl "en"). For US local SERPs, leave proxyMode as location so the service uses fresh residential proxy IDs across retries, briefly waits for browser-service automatic CAPTCHA/challenge solving when Google blocks a page, and rotates to a new proxy/session if the challenge does not clear or the market evidence is wrong. Use maxQuestions 30 normally, 100-200 for "full", "deep", "all", or comprehensive research. Deep harvests above 100 questions can run for several minutes with no interim progress \u2014 warn the user before starting one and keep maxQuestions at or below 100 unless they explicitly want a deep harvest. Credits are charged by extracted question; unused request hold is refunded.'),
|
|
1780
1835
|
inputSchema: HarvestPaaInputSchema,
|
|
1781
1836
|
outputSchema: HarvestPaaOutputSchema,
|
|
1782
1837
|
annotations: liveWebToolAnnotations("Google PAA + SERP Harvest")
|
|
1783
1838
|
}, async (input) => formatHarvestPaa(await executor.harvestPaa(input), input));
|
|
1784
1839
|
server.registerTool("search_serp", {
|
|
1785
1840
|
title: "Google SERP Lookup",
|
|
1786
|
-
description: withReportNote("Fast Google SERP lookup without PAA expansion. Use when the user asks for rankings, organic results, local pack, quick SERP, or positions. Split topic from location and infer gl/hl from the user request. For US city/state rankings, keep proxyMode as location and pass proxyZip when a city-center ZIP is known; location mode uses fresh residential proxy IDs
|
|
1841
|
+
description: withReportNote("Fast Google SERP lookup without PAA expansion. Use when the user asks for rankings, organic results, local pack, quick SERP, or positions. Split topic from location and infer gl/hl from the user request. For US city/state rankings, keep proxyMode as location and pass proxyZip when a city-center ZIP is known; location mode uses fresh residential proxy IDs, briefly waits for browser-service automatic CAPTCHA/challenge solving when Google blocks a page, and rotates to a new proxy/session if the challenge does not clear, the proxy tunnel fails, or location evidence is wrong."),
|
|
1787
1842
|
inputSchema: SearchSerpInputSchema,
|
|
1788
1843
|
outputSchema: SearchSerpOutputSchema,
|
|
1789
1844
|
annotations: liveWebToolAnnotations("Google SERP Lookup")
|
|
@@ -1851,14 +1906,14 @@ function registerPaaExtractorMcpTools(server, executor, options = {}) {
|
|
|
1851
1906
|
}, async (input) => formatMapsPlaceIntel(await executor.mapsPlaceIntel(input), input));
|
|
1852
1907
|
server.registerTool("maps_search", {
|
|
1853
1908
|
title: "Google Maps Business Search",
|
|
1854
|
-
description: withReportNote('Search Google Maps for multiple businesses/profiles by category, niche, keyword, or local market. Use this when the user asks for several Google Business Profiles, GMBs, GBPs, leads, prospects, competitors, or "more than the 3-pack." For US city/state Maps searches, keep proxyMode as location so the browser service
|
|
1909
|
+
description: withReportNote('Search Google Maps for multiple businesses/profiles by category, niche, keyword, or local market. Use this when the user asks for several Google Business Profiles, GMBs, GBPs, leads, prospects, competitors, or "more than the 3-pack." For US city/state Maps searches, keep proxyMode as location so the browser service creates a fresh residential proxy ID and browser session for the market; retryable failures rotate to a new proxy and new browser session for up to 5 attempts. Pass proxyZip only when a specific ZIP or city-center ZIP is known. Returns up to 50 candidates with names, place URLs, CIDs when available, ratings, review counts, profile metadata, and sanitized attempt telemetry. Default maxResults is 10; maximum is 50. Use maps_place_intel afterward only when a selected business needs full details and reviews.'),
|
|
1855
1910
|
inputSchema: MapsSearchInputSchema,
|
|
1856
1911
|
outputSchema: MapsSearchOutputSchema,
|
|
1857
1912
|
annotations: liveWebToolAnnotations("Google Maps Business Search")
|
|
1858
1913
|
}, async (input) => formatMapsSearch(await executor.mapsSearch(input), input));
|
|
1859
1914
|
server.registerTool("directory_workflow", {
|
|
1860
1915
|
title: "Directory Workflow: Markets + Maps",
|
|
1861
|
-
description: withReportNote('Build directory/prospecting datasets by selecting US city markets from the free Census Population Estimates city/place dataset, optionally joining configured US ZIPS/Lead Magician ZIP groups, then running Google Maps business searches for each city in parallel. Use this when the user wants "all cities over 100k population in a state", "build a directory CSV", "find markets then get Maps data", or similar location-database + Maps workflows. Set minPopulation, state, query, maxResultsPerCity, and concurrency. Use concurrency up to 5 for parallel city sessions. Keep proxyMode as location so each city
|
|
1916
|
+
description: withReportNote('Build directory/prospecting datasets by selecting US city markets from the free Census Population Estimates city/place dataset, optionally joining configured US ZIPS/Lead Magician ZIP groups, then running Google Maps business searches for each city in parallel. Use this when the user wants "all cities over 100k population in a state", "build a directory CSV", "find markets then get Maps data", or similar location-database + Maps workflows. Set minPopulation, state, query, maxResultsPerCity, and concurrency. Use concurrency up to 5 for parallel city sessions. Keep proxyMode as location so each city search creates fresh residential proxy IDs and browser sessions; retryable city failures rotate to a new proxy and new browser session for up to 5 attempts. Saved CSV rows include source_location, result_position, business_name, review_stars, category, address, phone, hours_status, website_url, directions_url, place_url, cid, cid_decimal, city population, and ZIP groups. Structured city results include sanitized attempt telemetry. This workflow captures star ratings from Maps list cards, not profile review counts; use maps_place_intel only when a selected profile needs deeper review details. For local Lead Magician ZIP enrichment, set MCP_SCRAPER_USZIPS_CSV_PATH on the API server or pass usZipsCsvPath only in local/test mode.'),
|
|
1862
1917
|
inputSchema: DirectoryWorkflowInputSchema,
|
|
1863
1918
|
outputSchema: DirectoryWorkflowOutputSchema,
|
|
1864
1919
|
annotations: liveWebToolAnnotations("Directory Workflow: Markets + Maps")
|
|
@@ -2005,4 +2060,4 @@ export {
|
|
|
2005
2060
|
registerPaaExtractorMcpTools,
|
|
2006
2061
|
HttpMcpToolExecutor
|
|
2007
2062
|
};
|
|
2008
|
-
//# sourceMappingURL=chunk-
|
|
2063
|
+
//# sourceMappingURL=chunk-6NEXSNSA.js.map
|