mcp-scraper 0.2.2 → 0.2.3

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.
@@ -17,7 +17,7 @@ loadDotEnv();
17
17
  async function main() {
18
18
  const [{ serve }, { app }, { startWorker }, { migrate }] = await Promise.all([
19
19
  import("@hono/node-server"),
20
- import("../server-3QMDOEOS.js"),
20
+ import("../server-SUSIMF72.js"),
21
21
  import("../worker-NAKGTIF5.js"),
22
22
  import("../db-YWCNHBLH.js")
23
23
  ]);
@@ -14,7 +14,7 @@ var import_node_os = require("os");
14
14
  var import_node_path = require("path");
15
15
 
16
16
  // src/version.ts
17
- var PACKAGE_VERSION = "0.2.2";
17
+ var PACKAGE_VERSION = "0.2.3";
18
18
 
19
19
  // src/mcp/browser-agent-tool-schemas.ts
20
20
  var import_zod = require("zod");
@@ -84,6 +84,11 @@ function replayFilePath(sessionId, replayId, filename) {
84
84
  return (0, import_node_path.join)(outputBaseDir(), "browser-replays", `${name}.mp4`);
85
85
  }
86
86
  function buildBrowserAgentMcpServer(opts) {
87
+ const server2 = new import_mcp.McpServer({ name: "browser-agent", version: PACKAGE_VERSION });
88
+ registerBrowserAgentMcpTools(server2, opts);
89
+ return server2;
90
+ }
91
+ function registerBrowserAgentMcpTools(server2, opts) {
87
92
  const baseUrl2 = opts.baseUrl.replace(/\/$/, "");
88
93
  const consoleBase = (opts.consoleBaseUrl ?? opts.baseUrl).replace(/\/$/, "");
89
94
  const timeoutMs = opts.timeoutMs ?? 9e4;
@@ -131,7 +136,6 @@ function buildBrowserAgentMcpServer(opts) {
131
136
  return { ok: false, data: { error: err instanceof Error ? err.message : String(err) } };
132
137
  }
133
138
  }
134
- const server2 = new import_mcp.McpServer({ name: "browser-agent", version: PACKAGE_VERSION });
135
139
  const annotations = (title, readOnly = false) => ({
136
140
  title,
137
141
  readOnlyHint: readOnly,
@@ -355,7 +359,6 @@ function buildBrowserAgentMcpServer(opts) {
355
359
  return textResult({ sessions });
356
360
  }
357
361
  );
358
- return server2;
359
362
  }
360
363
 
361
364
  // bin/browser-agent-stdio-server.ts
@@ -376,8 +379,8 @@ if (!apiKey) {
376
379
  process.stderr.write("MCP_SCRAPER_API_KEY env var or ~/.mcp-scraper-key is required\n");
377
380
  process.exit(1);
378
381
  }
379
- var baseUrl = process.env.MCP_SCRAPER_BASE_URL?.trim() ?? process.env.MCP_BASE_URL?.trim() ?? "https://mcpscraper.dev";
380
- var consoleBaseUrl = process.env.BROWSER_AGENT_CONSOLE_URL?.trim() ?? baseUrl;
382
+ var baseUrl = process.env.MCP_SCRAPER_BASE_URL?.trim() || process.env.MCP_BASE_URL?.trim() || "https://mcpscraper.dev";
383
+ var consoleBaseUrl = process.env.BROWSER_AGENT_CONSOLE_URL?.trim() || baseUrl;
381
384
  var server = buildBrowserAgentMcpServer({ baseUrl, apiKey, consoleBaseUrl });
382
385
  var transport = new import_stdio.StdioServerTransport();
383
386
  async function main() {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../bin/browser-agent-stdio-server.ts","../../src/mcp/browser-agent-mcp-server.ts","../../src/version.ts","../../src/mcp/browser-agent-tool-schemas.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { readFileSync } from 'node:fs'\nimport { homedir } from 'node:os'\nimport { join } from 'node:path'\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'\nimport { buildBrowserAgentMcpServer } from '../src/mcp/browser-agent-mcp-server.js'\n\nfunction readApiKeyFile(): string | undefined {\n const explicitPath = process.env.MCP_SCRAPER_KEY_PATH?.trim()\n const paths = [explicitPath, join(homedir(), '.mcp-scraper-key')].filter(Boolean) as string[]\n for (const path of paths) {\n try {\n const value = readFileSync(path, 'utf8').trim()\n if (value) return value\n } catch {\n void 0\n }\n }\n return undefined\n}\n\nconst apiKey = (\n process.env.MCP_SCRAPER_API_KEY ??\n process.env.MCP_SCRAPER_KEY ??\n process.env.MCP_API_KEY ??\n readApiKeyFile()\n)?.trim()\nif (!apiKey) {\n process.stderr.write('MCP_SCRAPER_API_KEY env var or ~/.mcp-scraper-key is required\\n')\n process.exit(1)\n}\n\nconst baseUrl = process.env.MCP_SCRAPER_BASE_URL?.trim() ?? process.env.MCP_BASE_URL?.trim() ?? 'https://mcpscraper.dev'\nconst consoleBaseUrl = process.env.BROWSER_AGENT_CONSOLE_URL?.trim() ?? baseUrl\nconst server = buildBrowserAgentMcpServer({ baseUrl, apiKey, consoleBaseUrl })\nconst transport = new StdioServerTransport()\n\nasync function main() {\n await server.connect(transport)\n}\n\nmain().catch(err => {\n process.stderr.write(`${err instanceof Error ? err.message : String(err)}\\n`)\n process.exit(1)\n})\n","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 BrowserGotoInputSchema,\n BrowserClickInputSchema,\n BrowserTypeInputSchema,\n BrowserScrollInputSchema,\n BrowserPressInputSchema,\n BrowserReplayStopInputSchema,\n BrowserReplayDownloadInputSchema,\n BrowserListInputSchema,\n} from './browser-agent-tool-schemas.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\nexport function buildBrowserAgentMcpServer(opts: BrowserAgentHttpOptions): McpServer {\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 server = new McpServer({ name: 'browser-agent', version: PACKAGE_VERSION })\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_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_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 return server\n}\n","export const PACKAGE_VERSION = '0.2.2'\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 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 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"],"mappings":"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","names":["import_node_fs","import_node_os","import_node_path","baseUrl","server"]}
1
+ {"version":3,"sources":["../../bin/browser-agent-stdio-server.ts","../../src/mcp/browser-agent-mcp-server.ts","../../src/version.ts","../../src/mcp/browser-agent-tool-schemas.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { readFileSync } from 'node:fs'\nimport { homedir } from 'node:os'\nimport { join } from 'node:path'\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'\nimport { buildBrowserAgentMcpServer } from '../src/mcp/browser-agent-mcp-server.js'\n\nfunction readApiKeyFile(): string | undefined {\n const explicitPath = process.env.MCP_SCRAPER_KEY_PATH?.trim()\n const paths = [explicitPath, join(homedir(), '.mcp-scraper-key')].filter(Boolean) as string[]\n for (const path of paths) {\n try {\n const value = readFileSync(path, 'utf8').trim()\n if (value) return value\n } catch {\n void 0\n }\n }\n return undefined\n}\n\nconst apiKey = (\n process.env.MCP_SCRAPER_API_KEY ??\n process.env.MCP_SCRAPER_KEY ??\n process.env.MCP_API_KEY ??\n readApiKeyFile()\n)?.trim()\nif (!apiKey) {\n process.stderr.write('MCP_SCRAPER_API_KEY env var or ~/.mcp-scraper-key is required\\n')\n process.exit(1)\n}\n\nconst baseUrl = process.env.MCP_SCRAPER_BASE_URL?.trim() || process.env.MCP_BASE_URL?.trim() || 'https://mcpscraper.dev'\nconst consoleBaseUrl = process.env.BROWSER_AGENT_CONSOLE_URL?.trim() || baseUrl\nconst server = buildBrowserAgentMcpServer({ baseUrl, apiKey, consoleBaseUrl })\nconst transport = new StdioServerTransport()\n\nasync function main() {\n await server.connect(transport)\n}\n\nmain().catch(err => {\n process.stderr.write(`${err instanceof Error ? err.message : String(err)}\\n`)\n process.exit(1)\n})\n","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 BrowserGotoInputSchema,\n BrowserClickInputSchema,\n BrowserTypeInputSchema,\n BrowserScrollInputSchema,\n BrowserPressInputSchema,\n BrowserReplayStopInputSchema,\n BrowserReplayDownloadInputSchema,\n BrowserListInputSchema,\n} from './browser-agent-tool-schemas.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\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_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_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","export const PACKAGE_VERSION = '0.2.3'\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 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 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"],"mappings":"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","names":["import_node_fs","import_node_os","import_node_path","server","baseUrl"]}
@@ -1,366 +1,17 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
- PACKAGE_VERSION
4
- } from "../chunk-PYBMZ346.js";
3
+ buildBrowserAgentMcpServer
4
+ } from "../chunk-E6IQRQ2T.js";
5
+ import "../chunk-RJ6I52AM.js";
5
6
 
6
7
  // bin/browser-agent-stdio-server.ts
7
8
  import { readFileSync } from "fs";
8
- import { homedir as homedir2 } from "os";
9
- import { join as join2 } from "path";
10
- import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
11
-
12
- // src/mcp/browser-agent-mcp-server.ts
13
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
14
- import { mkdirSync, writeFileSync } from "fs";
15
9
  import { homedir } from "os";
16
10
  import { join } from "path";
17
-
18
- // src/mcp/browser-agent-tool-schemas.ts
19
- import { z } from "zod";
20
- var BrowserOpenInputSchema = {
21
- label: z.string().optional().describe("Optional human label for this session, shown in the watch console."),
22
- url: z.string().url().optional().describe("Optional URL to navigate to immediately after opening."),
23
- profile: z.string().optional().describe("Optional saved profile name to load a logged-in session for a site."),
24
- 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.")
25
- };
26
- var BrowserSessionInputSchema = {
27
- session_id: z.string().describe("The session id returned by browser_open.")
28
- };
29
- var BrowserGotoInputSchema = {
30
- session_id: z.string().describe("The session id returned by browser_open."),
31
- url: z.string().url().describe("URL to navigate the browser to.")
32
- };
33
- var BrowserClickInputSchema = {
34
- session_id: z.string().describe("The session id returned by browser_open."),
35
- x: z.number().describe("X coordinate to click, in screenshot pixels. Use the x of an element from the latest screenshot."),
36
- y: z.number().describe("Y coordinate to click, in screenshot pixels."),
37
- button: z.enum(["left", "right", "middle"]).default("left").describe("Mouse button."),
38
- num_clicks: z.number().int().min(1).max(3).optional().describe("Number of clicks, e.g. 2 for double-click.")
39
- };
40
- var BrowserTypeInputSchema = {
41
- session_id: z.string().describe("The session id returned by browser_open."),
42
- text: z.string().describe("Text to type at the current focus. Click a field first to focus it."),
43
- delay: z.number().int().min(0).max(500).optional().describe("Optional per-keystroke delay in ms for human-like typing.")
44
- };
45
- var BrowserScrollInputSchema = {
46
- session_id: z.string().describe("The session id returned by browser_open."),
47
- delta_y: z.number().default(5).describe("Vertical scroll in wheel units. Positive scrolls down, negative up."),
48
- delta_x: z.number().default(0).describe("Horizontal scroll in wheel units."),
49
- x: z.number().optional().describe("X position to scroll at. Defaults to screen center."),
50
- y: z.number().optional().describe("Y position to scroll at. Defaults to screen center.")
51
- };
52
- var BrowserPressInputSchema = {
53
- session_id: z.string().describe("The session id returned by browser_open."),
54
- keys: z.array(z.string()).min(1).describe('Keys or combinations to press, e.g. ["Return"], ["Ctrl+a"], ["Ctrl+Shift+Tab"].')
55
- };
56
- var BrowserReplayStopInputSchema = {
57
- session_id: z.string().describe("The session id returned by browser_open."),
58
- replay_id: z.string().describe("The replay id returned by browser_replay_start.")
59
- };
60
- var BrowserReplayDownloadInputSchema = {
61
- session_id: z.string().describe("The session id returned by browser_open."),
62
- replay_id: z.string().describe("The replay id returned by browser_replay_start or browser_list_replays."),
63
- filename: z.string().optional().describe("Optional local MP4 filename. Defaults to a timestamped replay filename.")
64
- };
65
- var BrowserListInputSchema = {
66
- include_closed: z.boolean().default(false).describe("Include closed sessions in the list.")
67
- };
68
-
69
- // src/mcp/browser-agent-mcp-server.ts
70
- function textResult(value, isError = false) {
71
- return { content: [{ type: "text", text: JSON.stringify(value) }], isError };
72
- }
73
- function outputBaseDir() {
74
- return process.env.MCP_SCRAPER_OUTPUT_DIR?.trim() || join(homedir(), "Downloads", "mcp-scraper");
75
- }
76
- function safeFilePart(value) {
77
- return value.replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 120) || "replay";
78
- }
79
- function replayFilePath(sessionId, replayId, filename) {
80
- const requested = filename?.trim();
81
- const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
82
- const name = requested ? safeFilePart(requested).replace(/\.mp4$/i, "") : `${stamp}-${safeFilePart(sessionId)}-${safeFilePart(replayId)}`;
83
- return join(outputBaseDir(), "browser-replays", `${name}.mp4`);
84
- }
85
- function buildBrowserAgentMcpServer(opts) {
86
- const baseUrl2 = opts.baseUrl.replace(/\/$/, "");
87
- const consoleBase = (opts.consoleBaseUrl ?? opts.baseUrl).replace(/\/$/, "");
88
- const timeoutMs = opts.timeoutMs ?? 9e4;
89
- async function req(method, path, body) {
90
- try {
91
- const res = await fetch(`${baseUrl2}${path}`, {
92
- method,
93
- headers: { "Content-Type": "application/json", "x-api-key": opts.apiKey },
94
- body: body ? JSON.stringify(body) : void 0,
95
- signal: AbortSignal.timeout(timeoutMs)
96
- });
97
- const data = await res.json().catch(() => ({}));
98
- return { ok: res.ok, data };
99
- } catch (err) {
100
- return { ok: false, data: { error: err instanceof Error ? err.message : String(err) } };
101
- }
102
- }
103
- async function downloadReplay(sessionId, replayId, filename) {
104
- const path = `/agent/sessions/${encodeURIComponent(sessionId)}/replays/${encodeURIComponent(replayId)}/download`;
105
- try {
106
- const res = await fetch(`${baseUrl2}${path}`, {
107
- method: "GET",
108
- headers: { "x-api-key": opts.apiKey },
109
- signal: AbortSignal.timeout(timeoutMs)
110
- });
111
- if (!res.ok) {
112
- const data = await res.json().catch(async () => ({ error: await res.text().catch(() => `HTTP ${res.status}`) }));
113
- return { ok: false, data };
114
- }
115
- const bytes = Buffer.from(await res.arrayBuffer());
116
- const filePath = replayFilePath(sessionId, replayId, filename);
117
- mkdirSync(join(outputBaseDir(), "browser-replays"), { recursive: true });
118
- writeFileSync(filePath, bytes);
119
- return {
120
- ok: true,
121
- data: {
122
- replay_id: replayId,
123
- file_path: filePath,
124
- bytes: bytes.length,
125
- mime_type: res.headers.get("content-type") ?? "video/mp4",
126
- download_url: `${baseUrl2}${path}`
127
- }
128
- };
129
- } catch (err) {
130
- return { ok: false, data: { error: err instanceof Error ? err.message : String(err) } };
131
- }
132
- }
133
- const server2 = new McpServer({ name: "browser-agent", version: PACKAGE_VERSION });
134
- const annotations = (title, readOnly = false) => ({
135
- title,
136
- readOnlyHint: readOnly,
137
- destructiveHint: false,
138
- idempotentHint: false,
139
- openWorldHint: true
140
- });
141
- server2.registerTool(
142
- "browser_open",
143
- {
144
- title: "Open Browser Session",
145
- description: "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 \u2014 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.",
146
- inputSchema: BrowserOpenInputSchema,
147
- annotations: annotations("Open Browser Session")
148
- },
149
- async (input) => {
150
- const open = await req("POST", "/agent/sessions", {
151
- label: input.label,
152
- profile: input.profile,
153
- timeout_seconds: input.timeout_seconds
154
- });
155
- if (!open.ok) return textResult(open.data, true);
156
- const session = open.data;
157
- if (input.url) {
158
- await req("POST", `/agent/sessions/${session.session_id}/goto`, { url: input.url });
159
- }
160
- return textResult({
161
- session_id: session.session_id,
162
- watch_url: `${consoleBase}/console/${session.session_id}`,
163
- live_view_url: session.live_view_url ?? null,
164
- hint: "Call browser_screenshot to see the page. Click by the x,y of an element from the snapshot."
165
- });
166
- }
167
- );
168
- server2.registerTool(
169
- "browser_screenshot",
170
- {
171
- title: "See Page (Screenshot + Elements)",
172
- description: "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.",
173
- inputSchema: BrowserSessionInputSchema,
174
- annotations: annotations("See Page", true)
175
- },
176
- async (input) => {
177
- const res = await req("POST", `/agent/sessions/${input.session_id}/screenshot`);
178
- if (!res.ok) return textResult(res.data, true);
179
- const { image_base64, mime_type, url, title, elements, text } = res.data;
180
- const content = [];
181
- if (image_base64) content.push({ type: "image", data: image_base64, mimeType: mime_type ?? "image/png" });
182
- content.push({
183
- type: "text",
184
- text: JSON.stringify({ url, title, elements, text })
185
- });
186
- return { content };
187
- }
188
- );
189
- server2.registerTool(
190
- "browser_read",
191
- {
192
- title: "Read Page Text + Elements",
193
- description: "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.",
194
- inputSchema: BrowserSessionInputSchema,
195
- annotations: annotations("Read Page", true)
196
- },
197
- async (input) => {
198
- const res = await req("POST", `/agent/sessions/${input.session_id}/read`);
199
- return textResult(res.data, !res.ok);
200
- }
201
- );
202
- server2.registerTool(
203
- "browser_goto",
204
- {
205
- title: "Navigate To URL",
206
- description: "Navigate the browser to a URL. Follow with browser_screenshot to see the result.",
207
- inputSchema: BrowserGotoInputSchema,
208
- annotations: annotations("Navigate To URL")
209
- },
210
- async (input) => {
211
- const res = await req("POST", `/agent/sessions/${input.session_id}/goto`, { url: input.url });
212
- return textResult(res.data, !res.ok);
213
- }
214
- );
215
- server2.registerTool(
216
- "browser_click",
217
- {
218
- title: "Click",
219
- description: "Click at x,y (screenshot pixel coordinates). Use the x,y of a target element from the latest browser_screenshot or browser_read.",
220
- inputSchema: BrowserClickInputSchema,
221
- annotations: annotations("Click")
222
- },
223
- async (input) => {
224
- const res = await req("POST", `/agent/sessions/${input.session_id}/click`, {
225
- x: input.x,
226
- y: input.y,
227
- button: input.button,
228
- num_clicks: input.num_clicks
229
- });
230
- return textResult(res.data, !res.ok);
231
- }
232
- );
233
- server2.registerTool(
234
- "browser_type",
235
- {
236
- title: "Type Text",
237
- description: 'Type text at the current focus. Click an input field first to focus it. Use browser_press with ["Return"] to submit.',
238
- inputSchema: BrowserTypeInputSchema,
239
- annotations: annotations("Type Text")
240
- },
241
- async (input) => {
242
- const res = await req("POST", `/agent/sessions/${input.session_id}/type`, { text: input.text, delay: input.delay });
243
- return textResult(res.data, !res.ok);
244
- }
245
- );
246
- server2.registerTool(
247
- "browser_scroll",
248
- {
249
- title: "Scroll",
250
- description: "Scroll the page. Positive delta_y scrolls down. Follow with browser_screenshot to see newly revealed content.",
251
- inputSchema: BrowserScrollInputSchema,
252
- annotations: annotations("Scroll")
253
- },
254
- async (input) => {
255
- const res = await req("POST", `/agent/sessions/${input.session_id}/scroll`, {
256
- delta_y: input.delta_y,
257
- delta_x: input.delta_x,
258
- x: input.x,
259
- y: input.y
260
- });
261
- return textResult(res.data, !res.ok);
262
- }
263
- );
264
- server2.registerTool(
265
- "browser_press",
266
- {
267
- title: "Press Keys",
268
- description: 'Press keys or combinations, e.g. ["Return"] to submit, ["Ctrl+a"] to select all, ["Ctrl+Shift+Tab"] to switch tabs.',
269
- inputSchema: BrowserPressInputSchema,
270
- annotations: annotations("Press Keys")
271
- },
272
- async (input) => {
273
- const res = await req("POST", `/agent/sessions/${input.session_id}/press`, { keys: input.keys });
274
- return textResult(res.data, !res.ok);
275
- }
276
- );
277
- server2.registerTool(
278
- "browser_replay_start",
279
- {
280
- title: "Start Recording",
281
- 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.",
282
- inputSchema: BrowserSessionInputSchema,
283
- annotations: annotations("Start Recording")
284
- },
285
- async (input) => {
286
- const res = await req("POST", `/agent/sessions/${input.session_id}/replay/start`);
287
- return textResult(res.data, !res.ok);
288
- }
289
- );
290
- server2.registerTool(
291
- "browser_replay_stop",
292
- {
293
- title: "Stop Recording",
294
- description: "Stop a replay recording and expose the final view_url and download_url. Use browser_replay_download to save the MP4 locally.",
295
- inputSchema: BrowserReplayStopInputSchema,
296
- annotations: annotations("Stop Recording")
297
- },
298
- async (input) => {
299
- const res = await req("POST", `/agent/sessions/${input.session_id}/replay/stop`, { replay_id: input.replay_id });
300
- return textResult(res.data, !res.ok);
301
- }
302
- );
303
- server2.registerTool(
304
- "browser_list_replays",
305
- {
306
- title: "List Replay Videos",
307
- description: "List replay recordings for a browser session, including final view_url and authenticated download_url values when available.",
308
- inputSchema: BrowserSessionInputSchema,
309
- annotations: annotations("List Replay Videos", true)
310
- },
311
- async (input) => {
312
- const res = await req("GET", `/agent/sessions/${input.session_id}/replays`);
313
- return textResult(res.data, !res.ok);
314
- }
315
- );
316
- server2.registerTool(
317
- "browser_replay_download",
318
- {
319
- title: "Download Replay MP4",
320
- 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.",
321
- inputSchema: BrowserReplayDownloadInputSchema,
322
- annotations: annotations("Download Replay MP4", true)
323
- },
324
- async (input) => {
325
- const res = await downloadReplay(input.session_id, input.replay_id, input.filename);
326
- return textResult(res.data, !res.ok);
327
- }
328
- );
329
- server2.registerTool(
330
- "browser_close",
331
- {
332
- title: "Close Browser Session",
333
- description: "Close and release the browser session when the task is done.",
334
- inputSchema: BrowserSessionInputSchema,
335
- annotations: { title: "Close Browser Session", readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: false }
336
- },
337
- async (input) => {
338
- const res = await req("DELETE", `/agent/sessions/${input.session_id}`);
339
- return textResult(res.data, !res.ok);
340
- }
341
- );
342
- server2.registerTool(
343
- "browser_list_sessions",
344
- {
345
- title: "List Browser Sessions",
346
- description: "List your browser sessions and their status, with a watch_url for each.",
347
- inputSchema: BrowserListInputSchema,
348
- annotations: annotations("List Browser Sessions", true)
349
- },
350
- async (input) => {
351
- const res = await req("GET", `/agent/sessions${input.include_closed ? "?all=1" : ""}`);
352
- if (!res.ok) return textResult(res.data, true);
353
- const sessions = (res.data.sessions ?? []).map((s) => ({ ...s, watch_url: `${consoleBase}/console/${s.session_id}` }));
354
- return textResult({ sessions });
355
- }
356
- );
357
- return server2;
358
- }
359
-
360
- // bin/browser-agent-stdio-server.ts
11
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
361
12
  function readApiKeyFile() {
362
13
  const explicitPath = process.env.MCP_SCRAPER_KEY_PATH?.trim();
363
- const paths = [explicitPath, join2(homedir2(), ".mcp-scraper-key")].filter(Boolean);
14
+ const paths = [explicitPath, join(homedir(), ".mcp-scraper-key")].filter(Boolean);
364
15
  for (const path of paths) {
365
16
  try {
366
17
  const value = readFileSync(path, "utf8").trim();
@@ -375,8 +26,8 @@ if (!apiKey) {
375
26
  process.stderr.write("MCP_SCRAPER_API_KEY env var or ~/.mcp-scraper-key is required\n");
376
27
  process.exit(1);
377
28
  }
378
- var baseUrl = process.env.MCP_SCRAPER_BASE_URL?.trim() ?? process.env.MCP_BASE_URL?.trim() ?? "https://mcpscraper.dev";
379
- var consoleBaseUrl = process.env.BROWSER_AGENT_CONSOLE_URL?.trim() ?? baseUrl;
29
+ var baseUrl = process.env.MCP_SCRAPER_BASE_URL?.trim() || process.env.MCP_BASE_URL?.trim() || "https://mcpscraper.dev";
30
+ var consoleBaseUrl = process.env.BROWSER_AGENT_CONSOLE_URL?.trim() || baseUrl;
380
31
  var server = buildBrowserAgentMcpServer({ baseUrl, apiKey, consoleBaseUrl });
381
32
  var transport = new StdioServerTransport();
382
33
  async function main() {