mcp-scraper 0.1.9 → 0.2.0

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,8 +17,8 @@ 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-MTXAJG5J.js"),
21
- import("../worker-AUCXFHEL.js"),
20
+ import("../server-ASCMKUQ5.js"),
21
+ import("../worker-KJ4A7WIR.js"),
22
22
  import("../db-YWCNHBLH.js")
23
23
  ]);
24
24
  const PORT = parseInt(process.env.PORT ?? "3001");
@@ -0,0 +1,314 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+
4
+ // bin/browser-agent-stdio-server.ts
5
+ var import_node_fs = require("fs");
6
+ var import_node_os = require("os");
7
+ var import_node_path = require("path");
8
+ var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
9
+
10
+ // src/mcp/browser-agent-mcp-server.ts
11
+ var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
12
+
13
+ // src/version.ts
14
+ var PACKAGE_VERSION = "0.2.0";
15
+
16
+ // src/mcp/browser-agent-tool-schemas.ts
17
+ var import_zod = require("zod");
18
+ var BrowserOpenInputSchema = {
19
+ label: import_zod.z.string().optional().describe("Optional human label for this session, shown in the watch console."),
20
+ url: import_zod.z.string().url().optional().describe("Optional URL to navigate to immediately after opening."),
21
+ profile: import_zod.z.string().optional().describe("Optional saved profile name to load a logged-in session for a site."),
22
+ timeout_seconds: import_zod.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.")
23
+ };
24
+ var BrowserSessionInputSchema = {
25
+ session_id: import_zod.z.string().describe("The session id returned by browser_open.")
26
+ };
27
+ var BrowserGotoInputSchema = {
28
+ session_id: import_zod.z.string().describe("The session id returned by browser_open."),
29
+ url: import_zod.z.string().url().describe("URL to navigate the browser to.")
30
+ };
31
+ var BrowserClickInputSchema = {
32
+ session_id: import_zod.z.string().describe("The session id returned by browser_open."),
33
+ x: import_zod.z.number().describe("X coordinate to click, in screenshot pixels. Use the x of an element from the latest screenshot."),
34
+ y: import_zod.z.number().describe("Y coordinate to click, in screenshot pixels."),
35
+ button: import_zod.z.enum(["left", "right", "middle"]).default("left").describe("Mouse button."),
36
+ num_clicks: import_zod.z.number().int().min(1).max(3).optional().describe("Number of clicks, e.g. 2 for double-click.")
37
+ };
38
+ var BrowserTypeInputSchema = {
39
+ session_id: import_zod.z.string().describe("The session id returned by browser_open."),
40
+ text: import_zod.z.string().describe("Text to type at the current focus. Click a field first to focus it."),
41
+ delay: import_zod.z.number().int().min(0).max(500).optional().describe("Optional per-keystroke delay in ms for human-like typing.")
42
+ };
43
+ var BrowserScrollInputSchema = {
44
+ session_id: import_zod.z.string().describe("The session id returned by browser_open."),
45
+ delta_y: import_zod.z.number().default(5).describe("Vertical scroll in wheel units. Positive scrolls down, negative up."),
46
+ delta_x: import_zod.z.number().default(0).describe("Horizontal scroll in wheel units."),
47
+ x: import_zod.z.number().optional().describe("X position to scroll at. Defaults to screen center."),
48
+ y: import_zod.z.number().optional().describe("Y position to scroll at. Defaults to screen center.")
49
+ };
50
+ var BrowserPressInputSchema = {
51
+ session_id: import_zod.z.string().describe("The session id returned by browser_open."),
52
+ keys: import_zod.z.array(import_zod.z.string()).min(1).describe('Keys or combinations to press, e.g. ["Return"], ["Ctrl+a"], ["Ctrl+Shift+Tab"].')
53
+ };
54
+ var BrowserReplayStopInputSchema = {
55
+ session_id: import_zod.z.string().describe("The session id returned by browser_open."),
56
+ replay_id: import_zod.z.string().describe("The replay id returned by browser_replay_start.")
57
+ };
58
+ var BrowserListInputSchema = {
59
+ include_closed: import_zod.z.boolean().default(false).describe("Include closed sessions in the list.")
60
+ };
61
+
62
+ // src/mcp/browser-agent-mcp-server.ts
63
+ function textResult(value, isError = false) {
64
+ return { content: [{ type: "text", text: JSON.stringify(value) }], isError };
65
+ }
66
+ function buildBrowserAgentMcpServer(opts) {
67
+ const baseUrl2 = opts.baseUrl.replace(/\/$/, "");
68
+ const consoleBase = (opts.consoleBaseUrl ?? opts.baseUrl).replace(/\/$/, "");
69
+ const timeoutMs = opts.timeoutMs ?? 9e4;
70
+ async function req(method, path, body) {
71
+ try {
72
+ const res = await fetch(`${baseUrl2}${path}`, {
73
+ method,
74
+ headers: { "Content-Type": "application/json", "x-api-key": opts.apiKey },
75
+ body: body ? JSON.stringify(body) : void 0,
76
+ signal: AbortSignal.timeout(timeoutMs)
77
+ });
78
+ const data = await res.json().catch(() => ({}));
79
+ return { ok: res.ok, data };
80
+ } catch (err) {
81
+ return { ok: false, data: { error: err instanceof Error ? err.message : String(err) } };
82
+ }
83
+ }
84
+ const server2 = new import_mcp.McpServer({ name: "browser-agent", version: PACKAGE_VERSION });
85
+ const annotations = (title, readOnly = false) => ({
86
+ title,
87
+ readOnlyHint: readOnly,
88
+ destructiveHint: false,
89
+ idempotentHint: false,
90
+ openWorldHint: true
91
+ });
92
+ server2.registerTool(
93
+ "browser_open",
94
+ {
95
+ title: "Open Browser Session",
96
+ 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.",
97
+ inputSchema: BrowserOpenInputSchema,
98
+ annotations: annotations("Open Browser Session")
99
+ },
100
+ async (input) => {
101
+ const open = await req("POST", "/agent/sessions", {
102
+ label: input.label,
103
+ profile: input.profile,
104
+ timeout_seconds: input.timeout_seconds
105
+ });
106
+ if (!open.ok) return textResult(open.data, true);
107
+ const session = open.data;
108
+ if (input.url) {
109
+ await req("POST", `/agent/sessions/${session.session_id}/goto`, { url: input.url });
110
+ }
111
+ return textResult({
112
+ session_id: session.session_id,
113
+ watch_url: `${consoleBase}/console/${session.session_id}`,
114
+ hint: "Call browser_screenshot to see the page. Click by the x,y of an element from the snapshot."
115
+ });
116
+ }
117
+ );
118
+ server2.registerTool(
119
+ "browser_screenshot",
120
+ {
121
+ title: "See Page (Screenshot + Elements)",
122
+ 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.",
123
+ inputSchema: BrowserSessionInputSchema,
124
+ annotations: annotations("See Page", true)
125
+ },
126
+ async (input) => {
127
+ const res = await req("POST", `/agent/sessions/${input.session_id}/screenshot`);
128
+ if (!res.ok) return textResult(res.data, true);
129
+ const { image_base64, mime_type, url, title, elements, text } = res.data;
130
+ const content = [];
131
+ if (image_base64) content.push({ type: "image", data: image_base64, mimeType: mime_type ?? "image/png" });
132
+ content.push({
133
+ type: "text",
134
+ text: JSON.stringify({ url, title, elements, text })
135
+ });
136
+ return { content };
137
+ }
138
+ );
139
+ server2.registerTool(
140
+ "browser_read",
141
+ {
142
+ title: "Read Page Text + Elements",
143
+ 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.",
144
+ inputSchema: BrowserSessionInputSchema,
145
+ annotations: annotations("Read Page", true)
146
+ },
147
+ async (input) => {
148
+ const res = await req("POST", `/agent/sessions/${input.session_id}/read`);
149
+ return textResult(res.data, !res.ok);
150
+ }
151
+ );
152
+ server2.registerTool(
153
+ "browser_goto",
154
+ {
155
+ title: "Navigate To URL",
156
+ description: "Navigate the browser to a URL. Follow with browser_screenshot to see the result.",
157
+ inputSchema: BrowserGotoInputSchema,
158
+ annotations: annotations("Navigate To URL")
159
+ },
160
+ async (input) => {
161
+ const res = await req("POST", `/agent/sessions/${input.session_id}/goto`, { url: input.url });
162
+ return textResult(res.data, !res.ok);
163
+ }
164
+ );
165
+ server2.registerTool(
166
+ "browser_click",
167
+ {
168
+ title: "Click",
169
+ description: "Click at x,y (screenshot pixel coordinates). Use the x,y of a target element from the latest browser_screenshot or browser_read.",
170
+ inputSchema: BrowserClickInputSchema,
171
+ annotations: annotations("Click")
172
+ },
173
+ async (input) => {
174
+ const res = await req("POST", `/agent/sessions/${input.session_id}/click`, {
175
+ x: input.x,
176
+ y: input.y,
177
+ button: input.button,
178
+ num_clicks: input.num_clicks
179
+ });
180
+ return textResult(res.data, !res.ok);
181
+ }
182
+ );
183
+ server2.registerTool(
184
+ "browser_type",
185
+ {
186
+ title: "Type Text",
187
+ description: 'Type text at the current focus. Click an input field first to focus it. Use browser_press with ["Return"] to submit.',
188
+ inputSchema: BrowserTypeInputSchema,
189
+ annotations: annotations("Type Text")
190
+ },
191
+ async (input) => {
192
+ const res = await req("POST", `/agent/sessions/${input.session_id}/type`, { text: input.text, delay: input.delay });
193
+ return textResult(res.data, !res.ok);
194
+ }
195
+ );
196
+ server2.registerTool(
197
+ "browser_scroll",
198
+ {
199
+ title: "Scroll",
200
+ description: "Scroll the page. Positive delta_y scrolls down. Follow with browser_screenshot to see newly revealed content.",
201
+ inputSchema: BrowserScrollInputSchema,
202
+ annotations: annotations("Scroll")
203
+ },
204
+ async (input) => {
205
+ const res = await req("POST", `/agent/sessions/${input.session_id}/scroll`, {
206
+ delta_y: input.delta_y,
207
+ delta_x: input.delta_x,
208
+ x: input.x,
209
+ y: input.y
210
+ });
211
+ return textResult(res.data, !res.ok);
212
+ }
213
+ );
214
+ server2.registerTool(
215
+ "browser_press",
216
+ {
217
+ title: "Press Keys",
218
+ description: 'Press keys or combinations, e.g. ["Return"] to submit, ["Ctrl+a"] to select all, ["Ctrl+Shift+Tab"] to switch tabs.',
219
+ inputSchema: BrowserPressInputSchema,
220
+ annotations: annotations("Press Keys")
221
+ },
222
+ async (input) => {
223
+ const res = await req("POST", `/agent/sessions/${input.session_id}/press`, { keys: input.keys });
224
+ return textResult(res.data, !res.ok);
225
+ }
226
+ );
227
+ server2.registerTool(
228
+ "browser_replay_start",
229
+ {
230
+ title: "Start Recording",
231
+ description: "Start recording an MP4 replay of the session. Returns a replay_id. Use to capture a task for later review; stop with browser_replay_stop.",
232
+ inputSchema: BrowserSessionInputSchema,
233
+ annotations: annotations("Start Recording")
234
+ },
235
+ async (input) => {
236
+ const res = await req("POST", `/agent/sessions/${input.session_id}/replay/start`);
237
+ return textResult(res.data, !res.ok);
238
+ }
239
+ );
240
+ server2.registerTool(
241
+ "browser_replay_stop",
242
+ {
243
+ title: "Stop Recording",
244
+ description: "Stop a replay recording and persist the video. The recording becomes viewable in the watch console.",
245
+ inputSchema: BrowserReplayStopInputSchema,
246
+ annotations: annotations("Stop Recording")
247
+ },
248
+ async (input) => {
249
+ const res = await req("POST", `/agent/sessions/${input.session_id}/replay/stop`, { replay_id: input.replay_id });
250
+ return textResult(res.data, !res.ok);
251
+ }
252
+ );
253
+ server2.registerTool(
254
+ "browser_close",
255
+ {
256
+ title: "Close Browser Session",
257
+ description: "Close and release the browser session when the task is done.",
258
+ inputSchema: BrowserSessionInputSchema,
259
+ annotations: { title: "Close Browser Session", readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: false }
260
+ },
261
+ async (input) => {
262
+ const res = await req("DELETE", `/agent/sessions/${input.session_id}`);
263
+ return textResult(res.data, !res.ok);
264
+ }
265
+ );
266
+ server2.registerTool(
267
+ "browser_list_sessions",
268
+ {
269
+ title: "List Browser Sessions",
270
+ description: "List your browser sessions and their status, with a watch_url for each.",
271
+ inputSchema: BrowserListInputSchema,
272
+ annotations: annotations("List Browser Sessions", true)
273
+ },
274
+ async (input) => {
275
+ const res = await req("GET", `/agent/sessions${input.include_closed ? "?all=1" : ""}`);
276
+ if (!res.ok) return textResult(res.data, true);
277
+ const sessions = (res.data.sessions ?? []).map((s) => ({ ...s, watch_url: `${consoleBase}/console/${s.session_id}` }));
278
+ return textResult({ sessions });
279
+ }
280
+ );
281
+ return server2;
282
+ }
283
+
284
+ // bin/browser-agent-stdio-server.ts
285
+ function readApiKeyFile() {
286
+ const explicitPath = process.env.MCP_SCRAPER_KEY_PATH?.trim();
287
+ const paths = [explicitPath, (0, import_node_path.join)((0, import_node_os.homedir)(), ".mcp-scraper-key")].filter(Boolean);
288
+ for (const path of paths) {
289
+ try {
290
+ const value = (0, import_node_fs.readFileSync)(path, "utf8").trim();
291
+ if (value) return value;
292
+ } catch {
293
+ }
294
+ }
295
+ return void 0;
296
+ }
297
+ var apiKey = (process.env.MCP_SCRAPER_API_KEY ?? process.env.MCP_SCRAPER_KEY ?? process.env.MCP_API_KEY ?? readApiKeyFile())?.trim();
298
+ if (!apiKey) {
299
+ process.stderr.write("MCP_SCRAPER_API_KEY env var or ~/.mcp-scraper-key is required\n");
300
+ process.exit(1);
301
+ }
302
+ var baseUrl = process.env.MCP_SCRAPER_BASE_URL?.trim() ?? process.env.MCP_BASE_URL?.trim() ?? "https://mcpscraper.dev";
303
+ var consoleBaseUrl = process.env.BROWSER_AGENT_CONSOLE_URL?.trim() ?? baseUrl;
304
+ var server = buildBrowserAgentMcpServer({ baseUrl, apiKey, consoleBaseUrl });
305
+ var transport = new import_stdio.StdioServerTransport();
306
+ async function main() {
307
+ await server.connect(transport);
308
+ }
309
+ main().catch((err) => {
310
+ process.stderr.write(`${err instanceof Error ? err.message : String(err)}
311
+ `);
312
+ process.exit(1);
313
+ });
314
+ //# sourceMappingURL=browser-agent-stdio-server.cjs.map
@@ -0,0 +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 { PACKAGE_VERSION } from '../version.js'\nimport {\n BrowserOpenInputSchema,\n BrowserSessionInputSchema,\n BrowserGotoInputSchema,\n BrowserClickInputSchema,\n BrowserTypeInputSchema,\n BrowserScrollInputSchema,\n BrowserPressInputSchema,\n BrowserReplayStopInputSchema,\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\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 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 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 a replay_id. 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 persist the video. The recording becomes viewable in the watch console.',\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_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.0'\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 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":["baseUrl","server"]}
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node