mcp-scraper 0.2.18 → 0.2.20
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 +17 -11
- package/dist/bin/api-server.cjs +1020 -348
- package/dist/bin/api-server.cjs.map +1 -1
- package/dist/bin/api-server.js +3 -3
- package/dist/bin/browser-agent-stdio-server.cjs +314 -55
- 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-cli.cjs +321 -167
- package/dist/bin/mcp-scraper-cli.cjs.map +1 -1
- package/dist/bin/mcp-scraper-cli.js +3 -2
- package/dist/bin/mcp-scraper-cli.js.map +1 -1
- package/dist/bin/mcp-scraper-combined-stdio-server.cjs +1230 -528
- package/dist/bin/mcp-scraper-combined-stdio-server.cjs.map +1 -1
- package/dist/bin/mcp-scraper-combined-stdio-server.js +19 -3
- package/dist/bin/mcp-scraper-combined-stdio-server.js.map +1 -1
- package/dist/bin/mcp-scraper-install.cjs +8 -6
- package/dist/bin/mcp-scraper-install.cjs.map +1 -1
- package/dist/bin/mcp-scraper-install.js +5 -92
- package/dist/bin/mcp-scraper-install.js.map +1 -1
- package/dist/bin/mcp-stdio-server.cjs +726 -387
- package/dist/bin/mcp-stdio-server.cjs.map +1 -1
- package/dist/bin/mcp-stdio-server.js +3 -2
- package/dist/bin/mcp-stdio-server.js.map +1 -1
- package/dist/bin/paa-harvest.cjs +5 -3
- package/dist/bin/paa-harvest.cjs.map +1 -1
- package/dist/bin/paa-harvest.js +2 -1
- package/dist/bin/paa-harvest.js.map +1 -1
- package/dist/{chunk-TL7YTFLH.js → chunk-DBQDG7EH.js} +99 -27
- package/dist/chunk-DBQDG7EH.js.map +1 -0
- package/dist/{chunk-F44RBOJ5.js → chunk-H2R232HK.js} +216 -108
- package/dist/chunk-H2R232HK.js.map +1 -0
- package/dist/{chunk-P5PLQU3H.js → chunk-L4OWOUGR.js} +315 -56
- package/dist/chunk-L4OWOUGR.js.map +1 -0
- package/dist/chunk-LFATOGDF.js +111 -0
- package/dist/chunk-LFATOGDF.js.map +1 -0
- package/dist/chunk-MLNCKQ2B.js +96 -0
- package/dist/chunk-MLNCKQ2B.js.map +1 -0
- package/dist/{chunk-RJMCASQH.js → chunk-O5J7E4VX.js} +394 -57
- package/dist/chunk-O5J7E4VX.js.map +1 -0
- package/dist/{chunk-BSYPATSM.js → chunk-SXTXMFEQ.js} +2 -2
- package/dist/{chunk-CQTAKXBN.js → chunk-XGUDTDZ2.js} +7 -104
- package/dist/chunk-XGUDTDZ2.js.map +1 -0
- package/dist/chunk-XKUDVN2E.js +7 -0
- package/dist/chunk-XKUDVN2E.js.map +1 -0
- package/dist/{db-P5X6UQ3E.js → db-BE4JVB3V.js} +8 -2
- package/dist/index.cjs +5 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/{server-VVY5K44O.js → server-7NE45K2A.js} +211 -37
- package/dist/server-7NE45K2A.js.map +1 -0
- package/dist/{worker-OZSWIS3F.js → worker-MIYG2B2I.js} +5 -4
- package/dist/{worker-OZSWIS3F.js.map → worker-MIYG2B2I.js.map} +1 -1
- package/docs/adr/0001-in-page-graphql-interception-for-anti-bot-scraping.md +2 -2
- package/docs/mcp-tool-craft-lint.generated.md +51 -0
- package/docs/mcp-tool-manifest.generated.json +1406 -0
- package/docs/specs/local-competitive-audit-spec.md +1 -2
- package/docs/specs/mcp-tool-definition-quality-audit-spec.md +1602 -0
- package/package.json +4 -1
- package/dist/chunk-CQTAKXBN.js.map +0 -1
- package/dist/chunk-F44RBOJ5.js.map +0 -1
- package/dist/chunk-M5SAUO4K.js +0 -7
- package/dist/chunk-M5SAUO4K.js.map +0 -1
- package/dist/chunk-P5PLQU3H.js.map +0 -1
- package/dist/chunk-RJMCASQH.js.map +0 -1
- package/dist/chunk-TL7YTFLH.js.map +0 -1
- package/dist/server-VVY5K44O.js.map +0 -1
- /package/dist/{chunk-BSYPATSM.js.map → chunk-SXTXMFEQ.js.map} +0 -0
- /package/dist/{db-P5X6UQ3E.js.map → db-BE4JVB3V.js.map} +0 -0
|
@@ -14,10 +14,22 @@ var import_node_os2 = require("os");
|
|
|
14
14
|
var import_node_path2 = require("path");
|
|
15
15
|
|
|
16
16
|
// src/version.ts
|
|
17
|
-
var PACKAGE_VERSION = "0.2.
|
|
17
|
+
var PACKAGE_VERSION = "0.2.20";
|
|
18
18
|
|
|
19
19
|
// src/mcp/browser-agent-tool-schemas.ts
|
|
20
20
|
var import_zod = require("zod");
|
|
21
|
+
var NullableString = import_zod.z.string().nullable();
|
|
22
|
+
var BrowserRawObject = import_zod.z.record(import_zod.z.unknown());
|
|
23
|
+
var BrowserElementOutput = import_zod.z.record(import_zod.z.unknown());
|
|
24
|
+
var BrowserBaseOutput = {
|
|
25
|
+
ok: import_zod.z.boolean().describe("Whether the browser-agent action succeeded."),
|
|
26
|
+
tool: import_zod.z.string().describe("Browser Agent MCP tool that produced this response."),
|
|
27
|
+
session_id: NullableString.describe("Browser session id when the response is scoped to a session.")
|
|
28
|
+
};
|
|
29
|
+
var BrowserReplayBaseOutput = {
|
|
30
|
+
...BrowserBaseOutput,
|
|
31
|
+
replay_id: NullableString.describe("Replay id when the response is scoped to a replay.")
|
|
32
|
+
};
|
|
21
33
|
var BrowserOpenInputSchema = {
|
|
22
34
|
label: import_zod.z.string().optional().describe("Optional human label for this session, shown in the watch console."),
|
|
23
35
|
url: import_zod.z.string().url().optional().describe("Optional URL to navigate to immediately after opening."),
|
|
@@ -25,7 +37,7 @@ var BrowserOpenInputSchema = {
|
|
|
25
37
|
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.")
|
|
26
38
|
};
|
|
27
39
|
var BrowserSessionInputSchema = {
|
|
28
|
-
session_id: import_zod.z.string().describe("The session id returned by browser_open.")
|
|
40
|
+
session_id: import_zod.z.string().describe("The session id returned by browser_open or browser_list_sessions. Use only a returned session_id; do not construct one yourself.")
|
|
29
41
|
};
|
|
30
42
|
var BrowserLocateTargetSchema = import_zod.z.object({
|
|
31
43
|
name: import_zod.z.string().optional().describe("Optional label for this target, echoed in the result."),
|
|
@@ -37,43 +49,43 @@ var BrowserLocateTargetSchema = import_zod.z.object({
|
|
|
37
49
|
message: "target requires selector or text"
|
|
38
50
|
});
|
|
39
51
|
var BrowserLocateInputSchema = {
|
|
40
|
-
session_id: import_zod.z.string().describe("The session id returned by browser_open."),
|
|
52
|
+
session_id: import_zod.z.string().describe("The session id returned by browser_open or browser_list_sessions. Use only a returned session_id; do not construct one yourself."),
|
|
41
53
|
targets: import_zod.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.")
|
|
42
54
|
};
|
|
43
55
|
var BrowserGotoInputSchema = {
|
|
44
|
-
session_id: import_zod.z.string().describe("The session id returned by browser_open."),
|
|
56
|
+
session_id: import_zod.z.string().describe("The session id returned by browser_open or browser_list_sessions. Use only a returned session_id; do not construct one yourself."),
|
|
45
57
|
url: import_zod.z.string().url().describe("URL to navigate the browser to.")
|
|
46
58
|
};
|
|
47
59
|
var BrowserClickInputSchema = {
|
|
48
|
-
session_id: import_zod.z.string().describe("The session id returned by browser_open."),
|
|
49
|
-
x: import_zod.z.number().describe("X coordinate to click, in screenshot pixels. Use the
|
|
50
|
-
y: import_zod.z.number().describe("Y coordinate to click, in screenshot pixels."),
|
|
60
|
+
session_id: import_zod.z.string().describe("The session id returned by browser_open or browser_list_sessions. Use only a returned session_id; do not construct one yourself."),
|
|
61
|
+
x: import_zod.z.number().describe("X coordinate to click, in screenshot pixels. Use only coordinates from the latest browser_screenshot, browser_read, or browser_locate result; do not guess."),
|
|
62
|
+
y: import_zod.z.number().describe("Y coordinate to click, in screenshot pixels. Use only coordinates from the latest browser_screenshot, browser_read, or browser_locate result; do not guess."),
|
|
51
63
|
button: import_zod.z.enum(["left", "right", "middle"]).default("left").describe("Mouse button."),
|
|
52
64
|
num_clicks: import_zod.z.number().int().min(1).max(3).optional().describe("Number of clicks, e.g. 2 for double-click.")
|
|
53
65
|
};
|
|
54
66
|
var BrowserTypeInputSchema = {
|
|
55
|
-
session_id: import_zod.z.string().describe("The session id returned by browser_open."),
|
|
67
|
+
session_id: import_zod.z.string().describe("The session id returned by browser_open or browser_list_sessions. Use only a returned session_id; do not construct one yourself."),
|
|
56
68
|
text: import_zod.z.string().describe("Text to type at the current focus. Click a field first to focus it."),
|
|
57
69
|
delay: import_zod.z.number().int().min(0).max(500).optional().describe("Optional per-keystroke delay in ms for human-like typing.")
|
|
58
70
|
};
|
|
59
71
|
var BrowserScrollInputSchema = {
|
|
60
|
-
session_id: import_zod.z.string().describe("The session id returned by browser_open."),
|
|
72
|
+
session_id: import_zod.z.string().describe("The session id returned by browser_open or browser_list_sessions. Use only a returned session_id; do not construct one yourself."),
|
|
61
73
|
delta_y: import_zod.z.number().default(5).describe("Vertical scroll in wheel units. Positive scrolls down, negative up."),
|
|
62
74
|
delta_x: import_zod.z.number().default(0).describe("Horizontal scroll in wheel units."),
|
|
63
75
|
x: import_zod.z.number().optional().describe("X position to scroll at. Defaults to screen center."),
|
|
64
76
|
y: import_zod.z.number().optional().describe("Y position to scroll at. Defaults to screen center.")
|
|
65
77
|
};
|
|
66
78
|
var BrowserPressInputSchema = {
|
|
67
|
-
session_id: import_zod.z.string().describe("The session id returned by browser_open."),
|
|
79
|
+
session_id: import_zod.z.string().describe("The session id returned by browser_open or browser_list_sessions. Use only a returned session_id; do not construct one yourself."),
|
|
68
80
|
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"].')
|
|
69
81
|
};
|
|
70
82
|
var BrowserReplayStopInputSchema = {
|
|
71
|
-
session_id: import_zod.z.string().describe("The session id returned by browser_open."),
|
|
72
|
-
replay_id: import_zod.z.string().describe("The replay id returned by browser_replay_start.")
|
|
83
|
+
session_id: import_zod.z.string().describe("The session id returned by browser_open or browser_list_sessions. Use only a returned session_id; do not construct one yourself."),
|
|
84
|
+
replay_id: import_zod.z.string().describe("The replay id returned by browser_replay_start or browser_list_replays. Use only a returned replay_id; do not construct one yourself.")
|
|
73
85
|
};
|
|
74
86
|
var BrowserReplayDownloadInputSchema = {
|
|
75
|
-
session_id: import_zod.z.string().describe("The session id returned by browser_open."),
|
|
76
|
-
replay_id: import_zod.z.string().describe("The replay id returned by browser_replay_start or browser_list_replays."),
|
|
87
|
+
session_id: import_zod.z.string().describe("The session id returned by browser_open or browser_list_sessions. Use only a returned session_id; do not construct one yourself."),
|
|
88
|
+
replay_id: import_zod.z.string().describe("The replay id returned by browser_replay_start or browser_list_replays. Use only a returned replay_id; do not construct one yourself."),
|
|
77
89
|
filename: import_zod.z.string().optional().describe("Optional local MP4 filename. Defaults to a timestamped replay filename.")
|
|
78
90
|
};
|
|
79
91
|
var BrowserReplayAnnotationSchema = import_zod.z.object({
|
|
@@ -95,7 +107,7 @@ var BrowserReplayAnnotationSchema = import_zod.z.object({
|
|
|
95
107
|
thickness: import_zod.z.number().min(1).max(24).optional().describe("Stroke thickness in pixels. Defaults to 5.")
|
|
96
108
|
});
|
|
97
109
|
var BrowserReplayMarkInputSchema = {
|
|
98
|
-
session_id: import_zod.z.string().describe("The session id returned by browser_open. A replay must already be recording."),
|
|
110
|
+
session_id: import_zod.z.string().describe("The session id returned by browser_open or browser_list_sessions. Use only a returned session_id; do not construct one yourself. A replay must already be recording."),
|
|
99
111
|
target: BrowserLocateTargetSchema.describe("The exact DOM element or text range to mark in the current viewport."),
|
|
100
112
|
type: import_zod.z.enum(["box", "circle", "underline", "arrow"]).default("box").describe("Annotation style to generate. Labels are included on the returned annotation when label is provided."),
|
|
101
113
|
label: import_zod.z.string().max(120).optional().describe("Optional callout text to render near the target."),
|
|
@@ -106,8 +118,8 @@ var BrowserReplayMarkInputSchema = {
|
|
|
106
118
|
duration_seconds: import_zod.z.number().min(0.5).max(30).default(4).describe("How long the generated annotation should remain visible.")
|
|
107
119
|
};
|
|
108
120
|
var BrowserReplayAnnotateInputSchema = {
|
|
109
|
-
session_id: import_zod.z.string().describe("The session id returned by browser_open."),
|
|
110
|
-
replay_id: import_zod.z.string().describe("The replay id returned by browser_replay_start or browser_list_replays."),
|
|
121
|
+
session_id: import_zod.z.string().describe("The session id returned by browser_open or browser_list_sessions. Use only a returned session_id; do not construct one yourself."),
|
|
122
|
+
replay_id: import_zod.z.string().describe("The replay id returned by browser_replay_start or browser_list_replays. Use only a returned replay_id; do not construct one yourself."),
|
|
111
123
|
annotations: import_zod.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."),
|
|
112
124
|
filename: import_zod.z.string().optional().describe("Optional output MP4 filename. Defaults to a timestamped annotated replay filename."),
|
|
113
125
|
source_width: import_zod.z.number().positive().optional().describe("Width of the screenshot coordinate space used for annotations. Defaults to the replay video width."),
|
|
@@ -118,6 +130,113 @@ var BrowserReplayAnnotateInputSchema = {
|
|
|
118
130
|
var BrowserListInputSchema = {
|
|
119
131
|
include_closed: import_zod.z.boolean().default(false).describe("Include closed sessions in the list.")
|
|
120
132
|
};
|
|
133
|
+
var BrowserOpenOutputSchema = {
|
|
134
|
+
ok: import_zod.z.boolean(),
|
|
135
|
+
tool: import_zod.z.literal("browser_open"),
|
|
136
|
+
session_id: import_zod.z.string().describe("Session id returned by browser_open. Use only this exact value in later browser_* calls; do not construct one yourself."),
|
|
137
|
+
watch_url: import_zod.z.string().describe("Human watch/takeover URL for this browser session."),
|
|
138
|
+
live_view_url: NullableString.describe("Raw live-view URL from the browser service when available."),
|
|
139
|
+
url: NullableString.describe("Initial URL requested by the caller, when provided."),
|
|
140
|
+
hint: import_zod.z.string(),
|
|
141
|
+
raw: BrowserRawObject.optional()
|
|
142
|
+
};
|
|
143
|
+
var BrowserScreenshotOutputSchema = {
|
|
144
|
+
...BrowserBaseOutput,
|
|
145
|
+
tool: import_zod.z.literal("browser_screenshot"),
|
|
146
|
+
url: NullableString,
|
|
147
|
+
title: NullableString,
|
|
148
|
+
text: import_zod.z.string(),
|
|
149
|
+
elements: import_zod.z.array(BrowserElementOutput),
|
|
150
|
+
screenshot: import_zod.z.object({
|
|
151
|
+
mime_type: import_zod.z.string(),
|
|
152
|
+
inline: import_zod.z.boolean()
|
|
153
|
+
}).nullable()
|
|
154
|
+
};
|
|
155
|
+
var BrowserReadOutputSchema = {
|
|
156
|
+
...BrowserBaseOutput,
|
|
157
|
+
tool: import_zod.z.literal("browser_read"),
|
|
158
|
+
url: NullableString,
|
|
159
|
+
title: NullableString,
|
|
160
|
+
text: import_zod.z.string(),
|
|
161
|
+
elements: import_zod.z.array(BrowserElementOutput),
|
|
162
|
+
raw: BrowserRawObject.optional()
|
|
163
|
+
};
|
|
164
|
+
var BrowserLocateOutputSchema = {
|
|
165
|
+
...BrowserBaseOutput,
|
|
166
|
+
tool: import_zod.z.literal("browser_locate"),
|
|
167
|
+
url: NullableString,
|
|
168
|
+
title: NullableString,
|
|
169
|
+
viewport: BrowserRawObject.nullable(),
|
|
170
|
+
replay: BrowserRawObject.nullable(),
|
|
171
|
+
targets: import_zod.z.array(BrowserRawObject),
|
|
172
|
+
raw: BrowserRawObject.optional()
|
|
173
|
+
};
|
|
174
|
+
var BrowserActionOutputSchema = {
|
|
175
|
+
...BrowserBaseOutput,
|
|
176
|
+
result: BrowserRawObject.describe("Provider action result. Check ok and follow with browser_screenshot/browser_read when page state matters."),
|
|
177
|
+
nextRecommendedTool: import_zod.z.string().nullable()
|
|
178
|
+
};
|
|
179
|
+
var BrowserReplayStartOutputSchema = {
|
|
180
|
+
...BrowserReplayBaseOutput,
|
|
181
|
+
tool: import_zod.z.literal("browser_replay_start"),
|
|
182
|
+
view_url: NullableString,
|
|
183
|
+
download_url: NullableString,
|
|
184
|
+
raw: BrowserRawObject.optional()
|
|
185
|
+
};
|
|
186
|
+
var BrowserReplayStopOutputSchema = {
|
|
187
|
+
...BrowserReplayBaseOutput,
|
|
188
|
+
tool: import_zod.z.literal("browser_replay_stop"),
|
|
189
|
+
view_url: NullableString,
|
|
190
|
+
download_url: NullableString,
|
|
191
|
+
raw: BrowserRawObject.optional()
|
|
192
|
+
};
|
|
193
|
+
var BrowserListReplaysOutputSchema = {
|
|
194
|
+
...BrowserBaseOutput,
|
|
195
|
+
tool: import_zod.z.literal("browser_list_replays"),
|
|
196
|
+
replays: import_zod.z.array(BrowserRawObject),
|
|
197
|
+
count: import_zod.z.number().int().min(0)
|
|
198
|
+
};
|
|
199
|
+
var BrowserReplayDownloadOutputSchema = {
|
|
200
|
+
...BrowserReplayBaseOutput,
|
|
201
|
+
tool: import_zod.z.literal("browser_replay_download"),
|
|
202
|
+
file_path: NullableString,
|
|
203
|
+
bytes: import_zod.z.number().int().min(0).nullable(),
|
|
204
|
+
mime_type: NullableString,
|
|
205
|
+
download_url: NullableString
|
|
206
|
+
};
|
|
207
|
+
var BrowserReplayMarkOutputSchema = {
|
|
208
|
+
...BrowserReplayBaseOutput,
|
|
209
|
+
tool: import_zod.z.literal("browser_replay_mark"),
|
|
210
|
+
annotation: BrowserRawObject,
|
|
211
|
+
source_width: import_zod.z.number().nullable(),
|
|
212
|
+
source_height: import_zod.z.number().nullable(),
|
|
213
|
+
target: BrowserRawObject.nullable(),
|
|
214
|
+
hint: import_zod.z.string()
|
|
215
|
+
};
|
|
216
|
+
var BrowserReplayAnnotateOutputSchema = {
|
|
217
|
+
...BrowserReplayBaseOutput,
|
|
218
|
+
tool: import_zod.z.literal("browser_replay_annotate"),
|
|
219
|
+
source_file_path: NullableString,
|
|
220
|
+
annotated_file_path: NullableString,
|
|
221
|
+
bytes: import_zod.z.number().int().min(0).nullable(),
|
|
222
|
+
width: import_zod.z.number().int().min(0).nullable(),
|
|
223
|
+
height: import_zod.z.number().int().min(0).nullable(),
|
|
224
|
+
annotation_count: import_zod.z.number().int().min(0).nullable(),
|
|
225
|
+
mime_type: NullableString
|
|
226
|
+
};
|
|
227
|
+
var BrowserCloseOutputSchema = {
|
|
228
|
+
...BrowserBaseOutput,
|
|
229
|
+
tool: import_zod.z.literal("browser_close"),
|
|
230
|
+
closed: import_zod.z.boolean(),
|
|
231
|
+
raw: BrowserRawObject.optional()
|
|
232
|
+
};
|
|
233
|
+
var BrowserListSessionsOutputSchema = {
|
|
234
|
+
ok: import_zod.z.boolean(),
|
|
235
|
+
tool: import_zod.z.literal("browser_list_sessions"),
|
|
236
|
+
session_id: import_zod.z.null(),
|
|
237
|
+
sessions: import_zod.z.array(BrowserRawObject),
|
|
238
|
+
count: import_zod.z.number().int().min(0)
|
|
239
|
+
};
|
|
121
240
|
|
|
122
241
|
// src/mcp/replay-annotator.ts
|
|
123
242
|
var import_node_child_process = require("child_process");
|
|
@@ -380,8 +499,40 @@ async function annotateReplayVideo(inputFilePath, outputFilePath, options) {
|
|
|
380
499
|
}
|
|
381
500
|
|
|
382
501
|
// src/mcp/browser-agent-mcp-server.ts
|
|
383
|
-
function
|
|
384
|
-
return {
|
|
502
|
+
function structuredResult(value, isError = false) {
|
|
503
|
+
return {
|
|
504
|
+
content: [{ type: "text", text: JSON.stringify(value) }],
|
|
505
|
+
structuredContent: value,
|
|
506
|
+
isError
|
|
507
|
+
};
|
|
508
|
+
}
|
|
509
|
+
function errorMessage(value) {
|
|
510
|
+
if (value && typeof value === "object") {
|
|
511
|
+
const data = value;
|
|
512
|
+
if (typeof data.error === "string") return data.error;
|
|
513
|
+
if (typeof data.message === "string") return data.message;
|
|
514
|
+
}
|
|
515
|
+
return typeof value === "string" ? value : "Browser Agent request failed";
|
|
516
|
+
}
|
|
517
|
+
function errorResult(tool, value, sessionId = null, replayId = null) {
|
|
518
|
+
return structuredResult({
|
|
519
|
+
ok: false,
|
|
520
|
+
tool,
|
|
521
|
+
session_id: sessionId,
|
|
522
|
+
...replayId !== null ? { replay_id: replayId } : {},
|
|
523
|
+
error: errorMessage(value),
|
|
524
|
+
raw: value && typeof value === "object" ? value : { value }
|
|
525
|
+
}, true);
|
|
526
|
+
}
|
|
527
|
+
function actionResult(tool, sessionId, ok, data, nextRecommendedTool = "browser_screenshot") {
|
|
528
|
+
if (!ok) return errorResult(tool, data, sessionId);
|
|
529
|
+
return structuredResult({
|
|
530
|
+
ok: true,
|
|
531
|
+
tool,
|
|
532
|
+
session_id: sessionId,
|
|
533
|
+
result: data && typeof data === "object" ? data : { value: data },
|
|
534
|
+
nextRecommendedTool
|
|
535
|
+
});
|
|
385
536
|
}
|
|
386
537
|
function outputBaseDir() {
|
|
387
538
|
return process.env.MCP_SCRAPER_OUTPUT_DIR?.trim() || (0, import_node_path2.join)((0, import_node_os2.homedir)(), "Downloads", "mcp-scraper");
|
|
@@ -486,6 +637,7 @@ function registerBrowserAgentMcpTools(server2, opts) {
|
|
|
486
637
|
title: "Open Browser Session",
|
|
487
638
|
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.",
|
|
488
639
|
inputSchema: BrowserOpenInputSchema,
|
|
640
|
+
outputSchema: BrowserOpenOutputSchema,
|
|
489
641
|
annotations: annotations("Open Browser Session")
|
|
490
642
|
},
|
|
491
643
|
async (input) => {
|
|
@@ -494,16 +646,20 @@ function registerBrowserAgentMcpTools(server2, opts) {
|
|
|
494
646
|
profile: input.profile,
|
|
495
647
|
timeout_seconds: input.timeout_seconds
|
|
496
648
|
});
|
|
497
|
-
if (!open.ok) return
|
|
649
|
+
if (!open.ok) return errorResult("browser_open", open.data);
|
|
498
650
|
const session = open.data;
|
|
499
651
|
if (input.url) {
|
|
500
652
|
await req("POST", `/agent/sessions/${session.session_id}/goto`, { url: input.url });
|
|
501
653
|
}
|
|
502
|
-
return
|
|
654
|
+
return structuredResult({
|
|
655
|
+
ok: true,
|
|
656
|
+
tool: "browser_open",
|
|
503
657
|
session_id: session.session_id,
|
|
504
658
|
watch_url: `${consoleBase}/console/${session.session_id}`,
|
|
505
659
|
live_view_url: session.live_view_url ?? null,
|
|
506
|
-
|
|
660
|
+
url: input.url ?? null,
|
|
661
|
+
hint: "Call browser_screenshot to see the page. Click by the x,y of an element from the snapshot.",
|
|
662
|
+
raw: session
|
|
507
663
|
});
|
|
508
664
|
}
|
|
509
665
|
);
|
|
@@ -513,19 +669,30 @@ function registerBrowserAgentMcpTools(server2, opts) {
|
|
|
513
669
|
title: "See Page (Screenshot + Elements)",
|
|
514
670
|
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.",
|
|
515
671
|
inputSchema: BrowserSessionInputSchema,
|
|
672
|
+
outputSchema: BrowserScreenshotOutputSchema,
|
|
516
673
|
annotations: annotations("See Page", true)
|
|
517
674
|
},
|
|
518
675
|
async (input) => {
|
|
519
676
|
const res = await req("POST", `/agent/sessions/${input.session_id}/screenshot`);
|
|
520
|
-
if (!res.ok) return
|
|
677
|
+
if (!res.ok) return errorResult("browser_screenshot", res.data, input.session_id);
|
|
521
678
|
const { image_base64, mime_type, url, title, elements, text } = res.data;
|
|
522
679
|
const content = [];
|
|
523
680
|
if (image_base64) content.push({ type: "image", data: image_base64, mimeType: mime_type ?? "image/png" });
|
|
681
|
+
const structured = {
|
|
682
|
+
ok: true,
|
|
683
|
+
tool: "browser_screenshot",
|
|
684
|
+
session_id: input.session_id,
|
|
685
|
+
url: url ?? null,
|
|
686
|
+
title: title ?? null,
|
|
687
|
+
text: typeof text === "string" ? text : "",
|
|
688
|
+
elements: Array.isArray(elements) ? elements : [],
|
|
689
|
+
screenshot: image_base64 ? { mime_type: mime_type ?? "image/png", inline: true } : null
|
|
690
|
+
};
|
|
524
691
|
content.push({
|
|
525
692
|
type: "text",
|
|
526
|
-
text: JSON.stringify(
|
|
693
|
+
text: JSON.stringify(structured)
|
|
527
694
|
});
|
|
528
|
-
return { content };
|
|
695
|
+
return { content, structuredContent: structured };
|
|
529
696
|
}
|
|
530
697
|
);
|
|
531
698
|
server2.registerTool(
|
|
@@ -534,11 +701,22 @@ function registerBrowserAgentMcpTools(server2, opts) {
|
|
|
534
701
|
title: "Read Page Text + Elements",
|
|
535
702
|
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.",
|
|
536
703
|
inputSchema: BrowserSessionInputSchema,
|
|
704
|
+
outputSchema: BrowserReadOutputSchema,
|
|
537
705
|
annotations: annotations("Read Page", true)
|
|
538
706
|
},
|
|
539
707
|
async (input) => {
|
|
540
708
|
const res = await req("POST", `/agent/sessions/${input.session_id}/read`);
|
|
541
|
-
return
|
|
709
|
+
if (!res.ok) return errorResult("browser_read", res.data, input.session_id);
|
|
710
|
+
return structuredResult({
|
|
711
|
+
ok: true,
|
|
712
|
+
tool: "browser_read",
|
|
713
|
+
session_id: input.session_id,
|
|
714
|
+
url: res.data?.url ?? null,
|
|
715
|
+
title: res.data?.title ?? null,
|
|
716
|
+
text: typeof res.data?.text === "string" ? res.data.text : "",
|
|
717
|
+
elements: Array.isArray(res.data?.elements) ? res.data.elements : [],
|
|
718
|
+
raw: res.data
|
|
719
|
+
});
|
|
542
720
|
}
|
|
543
721
|
);
|
|
544
722
|
server2.registerTool(
|
|
@@ -547,32 +725,46 @@ function registerBrowserAgentMcpTools(server2, opts) {
|
|
|
547
725
|
title: "Locate DOM Targets",
|
|
548
726
|
description: "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.",
|
|
549
727
|
inputSchema: BrowserLocateInputSchema,
|
|
728
|
+
outputSchema: BrowserLocateOutputSchema,
|
|
550
729
|
annotations: annotations("Locate DOM Targets", true)
|
|
551
730
|
},
|
|
552
731
|
async (input) => {
|
|
553
732
|
const res = await req("POST", `/agent/sessions/${input.session_id}/locate`, { targets: input.targets });
|
|
554
|
-
return
|
|
733
|
+
if (!res.ok) return errorResult("browser_locate", res.data, input.session_id);
|
|
734
|
+
return structuredResult({
|
|
735
|
+
ok: true,
|
|
736
|
+
tool: "browser_locate",
|
|
737
|
+
session_id: input.session_id,
|
|
738
|
+
url: res.data?.url ?? null,
|
|
739
|
+
title: res.data?.title ?? null,
|
|
740
|
+
viewport: res.data?.viewport ?? null,
|
|
741
|
+
replay: res.data?.replay ?? null,
|
|
742
|
+
targets: Array.isArray(res.data?.targets) ? res.data.targets : [],
|
|
743
|
+
raw: res.data
|
|
744
|
+
});
|
|
555
745
|
}
|
|
556
746
|
);
|
|
557
747
|
server2.registerTool(
|
|
558
748
|
"browser_goto",
|
|
559
749
|
{
|
|
560
750
|
title: "Navigate To URL",
|
|
561
|
-
description: "Navigate
|
|
751
|
+
description: "Navigate an existing browser session to a URL when the user asks you to go to another page or continue browsing in the same session. Use browser_open first if no session exists. Follow with browser_screenshot to see redirects, login walls, CAPTCHA/Cloudflare state, or the loaded page.",
|
|
562
752
|
inputSchema: BrowserGotoInputSchema,
|
|
753
|
+
outputSchema: BrowserActionOutputSchema,
|
|
563
754
|
annotations: annotations("Navigate To URL")
|
|
564
755
|
},
|
|
565
756
|
async (input) => {
|
|
566
757
|
const res = await req("POST", `/agent/sessions/${input.session_id}/goto`, { url: input.url });
|
|
567
|
-
return
|
|
758
|
+
return actionResult("browser_goto", input.session_id, res.ok, res.data, "browser_screenshot");
|
|
568
759
|
}
|
|
569
760
|
);
|
|
570
761
|
server2.registerTool(
|
|
571
762
|
"browser_click",
|
|
572
763
|
{
|
|
573
764
|
title: "Click",
|
|
574
|
-
description: "Click
|
|
765
|
+
description: "Click a visible page target using screenshot pixel coordinates. Use this when the user asks you to press a button, open a menu, choose a result, or activate a visible UI element. Use x/y only from the latest browser_screenshot, browser_read, or browser_locate result; do not guess coordinates. Follow with browser_screenshot when the click may change the page.",
|
|
575
766
|
inputSchema: BrowserClickInputSchema,
|
|
767
|
+
outputSchema: BrowserActionOutputSchema,
|
|
576
768
|
annotations: annotations("Click")
|
|
577
769
|
},
|
|
578
770
|
async (input) => {
|
|
@@ -582,28 +774,30 @@ function registerBrowserAgentMcpTools(server2, opts) {
|
|
|
582
774
|
button: input.button,
|
|
583
775
|
num_clicks: input.num_clicks
|
|
584
776
|
});
|
|
585
|
-
return
|
|
777
|
+
return actionResult("browser_click", input.session_id, res.ok, res.data, "browser_screenshot");
|
|
586
778
|
}
|
|
587
779
|
);
|
|
588
780
|
server2.registerTool(
|
|
589
781
|
"browser_type",
|
|
590
782
|
{
|
|
591
783
|
title: "Type Text",
|
|
592
|
-
description: 'Type text
|
|
784
|
+
description: 'Type text into the currently focused browser field. Use this when the user asks you to fill a search box, form field, login field, or editable text area. Click or Tab to the field first if focus is uncertain. Use browser_press with ["Return"] to submit, and browser_screenshot afterward when page state matters.',
|
|
593
785
|
inputSchema: BrowserTypeInputSchema,
|
|
786
|
+
outputSchema: BrowserActionOutputSchema,
|
|
594
787
|
annotations: annotations("Type Text")
|
|
595
788
|
},
|
|
596
789
|
async (input) => {
|
|
597
790
|
const res = await req("POST", `/agent/sessions/${input.session_id}/type`, { text: input.text, delay: input.delay });
|
|
598
|
-
return
|
|
791
|
+
return actionResult("browser_type", input.session_id, res.ok, res.data, "browser_screenshot");
|
|
599
792
|
}
|
|
600
793
|
);
|
|
601
794
|
server2.registerTool(
|
|
602
795
|
"browser_scroll",
|
|
603
796
|
{
|
|
604
797
|
title: "Scroll",
|
|
605
|
-
description: "Scroll the page. Positive delta_y scrolls down. Follow with browser_screenshot to
|
|
798
|
+
description: "Scroll the page to reveal more content before reading, clicking, or locating elements. Positive delta_y scrolls down; negative delta_y scrolls up. Follow with browser_screenshot or browser_read to inspect newly revealed content.",
|
|
606
799
|
inputSchema: BrowserScrollInputSchema,
|
|
800
|
+
outputSchema: BrowserActionOutputSchema,
|
|
607
801
|
annotations: annotations("Scroll")
|
|
608
802
|
},
|
|
609
803
|
async (input) => {
|
|
@@ -613,20 +807,21 @@ function registerBrowserAgentMcpTools(server2, opts) {
|
|
|
613
807
|
x: input.x,
|
|
614
808
|
y: input.y
|
|
615
809
|
});
|
|
616
|
-
return
|
|
810
|
+
return actionResult("browser_scroll", input.session_id, res.ok, res.data, "browser_screenshot");
|
|
617
811
|
}
|
|
618
812
|
);
|
|
619
813
|
server2.registerTool(
|
|
620
814
|
"browser_press",
|
|
621
815
|
{
|
|
622
816
|
title: "Press Keys",
|
|
623
|
-
description: 'Press keys or combinations,
|
|
817
|
+
description: 'Press keyboard keys or combinations in the active browser session. Use this for submit, Escape, Tab navigation, select-all, or keyboard shortcuts. Examples: ["Return"], ["Escape"], ["Tab"], ["Ctrl+a"], ["Ctrl+Shift+Tab"]. Use browser_type for text entry and browser_screenshot after keypresses that may change the page.',
|
|
624
818
|
inputSchema: BrowserPressInputSchema,
|
|
819
|
+
outputSchema: BrowserActionOutputSchema,
|
|
625
820
|
annotations: annotations("Press Keys")
|
|
626
821
|
},
|
|
627
822
|
async (input) => {
|
|
628
823
|
const res = await req("POST", `/agent/sessions/${input.session_id}/press`, { keys: input.keys });
|
|
629
|
-
return
|
|
824
|
+
return actionResult("browser_press", input.session_id, res.ok, res.data, "browser_screenshot");
|
|
630
825
|
}
|
|
631
826
|
);
|
|
632
827
|
server2.registerTool(
|
|
@@ -635,11 +830,21 @@ function registerBrowserAgentMcpTools(server2, opts) {
|
|
|
635
830
|
title: "Start Recording",
|
|
636
831
|
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.",
|
|
637
832
|
inputSchema: BrowserSessionInputSchema,
|
|
833
|
+
outputSchema: BrowserReplayStartOutputSchema,
|
|
638
834
|
annotations: annotations("Start Recording")
|
|
639
835
|
},
|
|
640
836
|
async (input) => {
|
|
641
837
|
const res = await req("POST", `/agent/sessions/${input.session_id}/replay/start`);
|
|
642
|
-
return
|
|
838
|
+
if (!res.ok) return errorResult("browser_replay_start", res.data, input.session_id);
|
|
839
|
+
return structuredResult({
|
|
840
|
+
ok: true,
|
|
841
|
+
tool: "browser_replay_start",
|
|
842
|
+
session_id: input.session_id,
|
|
843
|
+
replay_id: res.data?.replay_id ?? res.data?.replayId ?? null,
|
|
844
|
+
view_url: res.data?.view_url ?? res.data?.viewUrl ?? null,
|
|
845
|
+
download_url: res.data?.download_url ?? res.data?.downloadUrl ?? null,
|
|
846
|
+
raw: res.data
|
|
847
|
+
});
|
|
643
848
|
}
|
|
644
849
|
);
|
|
645
850
|
server2.registerTool(
|
|
@@ -648,11 +853,21 @@ function registerBrowserAgentMcpTools(server2, opts) {
|
|
|
648
853
|
title: "Stop Recording",
|
|
649
854
|
description: "Stop a replay recording and expose the final view_url and download_url. Use browser_replay_download to save the MP4 locally.",
|
|
650
855
|
inputSchema: BrowserReplayStopInputSchema,
|
|
856
|
+
outputSchema: BrowserReplayStopOutputSchema,
|
|
651
857
|
annotations: annotations("Stop Recording")
|
|
652
858
|
},
|
|
653
859
|
async (input) => {
|
|
654
860
|
const res = await req("POST", `/agent/sessions/${input.session_id}/replay/stop`, { replay_id: input.replay_id });
|
|
655
|
-
return
|
|
861
|
+
if (!res.ok) return errorResult("browser_replay_stop", res.data, input.session_id, input.replay_id);
|
|
862
|
+
return structuredResult({
|
|
863
|
+
ok: true,
|
|
864
|
+
tool: "browser_replay_stop",
|
|
865
|
+
session_id: input.session_id,
|
|
866
|
+
replay_id: res.data?.replay_id ?? res.data?.replayId ?? input.replay_id,
|
|
867
|
+
view_url: res.data?.view_url ?? res.data?.viewUrl ?? null,
|
|
868
|
+
download_url: res.data?.download_url ?? res.data?.downloadUrl ?? null,
|
|
869
|
+
raw: res.data
|
|
870
|
+
});
|
|
656
871
|
}
|
|
657
872
|
);
|
|
658
873
|
server2.registerTool(
|
|
@@ -661,11 +876,20 @@ function registerBrowserAgentMcpTools(server2, opts) {
|
|
|
661
876
|
title: "List Replay Videos",
|
|
662
877
|
description: "List replay recordings for a browser session, including final view_url and authenticated download_url values when available.",
|
|
663
878
|
inputSchema: BrowserSessionInputSchema,
|
|
879
|
+
outputSchema: BrowserListReplaysOutputSchema,
|
|
664
880
|
annotations: annotations("List Replay Videos", true)
|
|
665
881
|
},
|
|
666
882
|
async (input) => {
|
|
667
883
|
const res = await req("GET", `/agent/sessions/${input.session_id}/replays`);
|
|
668
|
-
return
|
|
884
|
+
if (!res.ok) return errorResult("browser_list_replays", res.data, input.session_id);
|
|
885
|
+
const replays = Array.isArray(res.data?.replays) ? res.data.replays : [];
|
|
886
|
+
return structuredResult({
|
|
887
|
+
ok: true,
|
|
888
|
+
tool: "browser_list_replays",
|
|
889
|
+
session_id: input.session_id,
|
|
890
|
+
replays,
|
|
891
|
+
count: replays.length
|
|
892
|
+
});
|
|
669
893
|
}
|
|
670
894
|
);
|
|
671
895
|
server2.registerTool(
|
|
@@ -674,11 +898,22 @@ function registerBrowserAgentMcpTools(server2, opts) {
|
|
|
674
898
|
title: "Download Replay MP4",
|
|
675
899
|
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.",
|
|
676
900
|
inputSchema: BrowserReplayDownloadInputSchema,
|
|
677
|
-
|
|
901
|
+
outputSchema: BrowserReplayDownloadOutputSchema,
|
|
902
|
+
annotations: annotations("Download Replay MP4")
|
|
678
903
|
},
|
|
679
904
|
async (input) => {
|
|
680
905
|
const res = await downloadReplay(input.session_id, input.replay_id, input.filename);
|
|
681
|
-
return
|
|
906
|
+
if (!res.ok) return errorResult("browser_replay_download", res.data, input.session_id, input.replay_id);
|
|
907
|
+
return structuredResult({
|
|
908
|
+
ok: true,
|
|
909
|
+
tool: "browser_replay_download",
|
|
910
|
+
session_id: input.session_id,
|
|
911
|
+
replay_id: input.replay_id,
|
|
912
|
+
file_path: res.data?.file_path ?? null,
|
|
913
|
+
bytes: typeof res.data?.bytes === "number" ? res.data.bytes : null,
|
|
914
|
+
mime_type: res.data?.mime_type ?? null,
|
|
915
|
+
download_url: res.data?.download_url ?? null
|
|
916
|
+
});
|
|
682
917
|
}
|
|
683
918
|
);
|
|
684
919
|
server2.registerTool(
|
|
@@ -687,19 +922,20 @@ function registerBrowserAgentMcpTools(server2, opts) {
|
|
|
687
922
|
title: "Mark Replay Annotation",
|
|
688
923
|
description: "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.",
|
|
689
924
|
inputSchema: BrowserReplayMarkInputSchema,
|
|
925
|
+
outputSchema: BrowserReplayMarkOutputSchema,
|
|
690
926
|
annotations: annotations("Mark Replay Annotation", true)
|
|
691
927
|
},
|
|
692
928
|
async (input) => {
|
|
693
929
|
const res = await req("POST", `/agent/sessions/${input.session_id}/locate`, { targets: [input.target] });
|
|
694
|
-
if (!res.ok) return
|
|
930
|
+
if (!res.ok) return errorResult("browser_replay_mark", res.data, input.session_id);
|
|
695
931
|
const target = res.data?.targets?.[0];
|
|
696
932
|
const element = target?.element;
|
|
697
933
|
const elapsed = res.data?.replay?.replay_elapsed_seconds;
|
|
698
934
|
if (!target?.found || !element) {
|
|
699
|
-
return
|
|
935
|
+
return errorResult("browser_replay_mark", { error: target?.error ?? "target not found in current viewport", target }, input.session_id);
|
|
700
936
|
}
|
|
701
937
|
if (!finiteNumber2(elapsed)) {
|
|
702
|
-
return
|
|
938
|
+
return errorResult("browser_replay_mark", { error: "no active replay clock found; call browser_replay_start before browser_replay_mark" }, input.session_id);
|
|
703
939
|
}
|
|
704
940
|
const padded = expandElementBounds(element, res.data?.viewport, input.padding ?? 8);
|
|
705
941
|
const start = Math.max(0, elapsed + (input.start_offset_seconds ?? -0.25));
|
|
@@ -716,7 +952,11 @@ function registerBrowserAgentMcpTools(server2, opts) {
|
|
|
716
952
|
...input.color ? { color: input.color } : {},
|
|
717
953
|
...input.thickness ? { thickness: input.thickness } : {}
|
|
718
954
|
};
|
|
719
|
-
return
|
|
955
|
+
return structuredResult({
|
|
956
|
+
ok: true,
|
|
957
|
+
tool: "browser_replay_mark",
|
|
958
|
+
session_id: input.session_id,
|
|
959
|
+
replay_id: res.data?.replay?.replay_id ?? res.data?.replay?.replayId ?? null,
|
|
720
960
|
annotation,
|
|
721
961
|
source_width: padded.sourceWidth,
|
|
722
962
|
source_height: padded.sourceHeight,
|
|
@@ -732,12 +972,13 @@ function registerBrowserAgentMcpTools(server2, opts) {
|
|
|
732
972
|
title: "Annotate Replay MP4",
|
|
733
973
|
description: "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.",
|
|
734
974
|
inputSchema: BrowserReplayAnnotateInputSchema,
|
|
735
|
-
|
|
975
|
+
outputSchema: BrowserReplayAnnotateOutputSchema,
|
|
976
|
+
annotations: annotations("Annotate Replay MP4")
|
|
736
977
|
},
|
|
737
978
|
async (input) => {
|
|
738
979
|
const sourceName = input.filename ? `${input.filename}-source` : void 0;
|
|
739
980
|
const downloaded = await downloadReplay(input.session_id, input.replay_id, sourceName);
|
|
740
|
-
if (!downloaded.ok) return
|
|
981
|
+
if (!downloaded.ok) return errorResult("browser_replay_annotate", downloaded.data, input.session_id, input.replay_id);
|
|
741
982
|
try {
|
|
742
983
|
const sourcePath = String(downloaded.data.file_path);
|
|
743
984
|
const outputPath = annotatedReplayFilePath(input.session_id, input.replay_id, input.filename);
|
|
@@ -749,7 +990,10 @@ function registerBrowserAgentMcpTools(server2, opts) {
|
|
|
749
990
|
sourceLeftOffset: input.source_left_offset,
|
|
750
991
|
sourceTopOffset: input.source_top_offset
|
|
751
992
|
});
|
|
752
|
-
return
|
|
993
|
+
return structuredResult({
|
|
994
|
+
ok: true,
|
|
995
|
+
tool: "browser_replay_annotate",
|
|
996
|
+
session_id: input.session_id,
|
|
753
997
|
replay_id: input.replay_id,
|
|
754
998
|
source_file_path: sourcePath,
|
|
755
999
|
annotated_file_path: result.filePath,
|
|
@@ -760,7 +1004,7 @@ function registerBrowserAgentMcpTools(server2, opts) {
|
|
|
760
1004
|
mime_type: "video/mp4"
|
|
761
1005
|
});
|
|
762
1006
|
} catch (err) {
|
|
763
|
-
return
|
|
1007
|
+
return errorResult("browser_replay_annotate", { error: err instanceof Error ? err.message : String(err) }, input.session_id, input.replay_id);
|
|
764
1008
|
}
|
|
765
1009
|
}
|
|
766
1010
|
);
|
|
@@ -768,28 +1012,43 @@ function registerBrowserAgentMcpTools(server2, opts) {
|
|
|
768
1012
|
"browser_close",
|
|
769
1013
|
{
|
|
770
1014
|
title: "Close Browser Session",
|
|
771
|
-
description: "Close and release
|
|
1015
|
+
description: "Close and release a browser session when the task is done, when the user asks to stop the browser, or when active browser billing should end. Use browser_list_sessions first if you need to recover the session_id.",
|
|
772
1016
|
inputSchema: BrowserSessionInputSchema,
|
|
1017
|
+
outputSchema: BrowserCloseOutputSchema,
|
|
773
1018
|
annotations: { title: "Close Browser Session", readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: false }
|
|
774
1019
|
},
|
|
775
1020
|
async (input) => {
|
|
776
1021
|
const res = await req("DELETE", `/agent/sessions/${input.session_id}`);
|
|
777
|
-
return
|
|
1022
|
+
if (!res.ok) return errorResult("browser_close", res.data, input.session_id);
|
|
1023
|
+
return structuredResult({
|
|
1024
|
+
ok: true,
|
|
1025
|
+
tool: "browser_close",
|
|
1026
|
+
session_id: input.session_id,
|
|
1027
|
+
closed: true,
|
|
1028
|
+
raw: res.data
|
|
1029
|
+
});
|
|
778
1030
|
}
|
|
779
1031
|
);
|
|
780
1032
|
server2.registerTool(
|
|
781
1033
|
"browser_list_sessions",
|
|
782
1034
|
{
|
|
783
1035
|
title: "List Browser Sessions",
|
|
784
|
-
description: "List
|
|
1036
|
+
description: "List browser sessions and their status, with a watch_url for each. Use this to recover a session_id, find an active browser, or decide which session to close. Use browser_open to create a new session.",
|
|
785
1037
|
inputSchema: BrowserListInputSchema,
|
|
1038
|
+
outputSchema: BrowserListSessionsOutputSchema,
|
|
786
1039
|
annotations: annotations("List Browser Sessions", true)
|
|
787
1040
|
},
|
|
788
1041
|
async (input) => {
|
|
789
1042
|
const res = await req("GET", `/agent/sessions${input.include_closed ? "?all=1" : ""}`);
|
|
790
|
-
if (!res.ok) return
|
|
1043
|
+
if (!res.ok) return errorResult("browser_list_sessions", res.data);
|
|
791
1044
|
const sessions = (res.data.sessions ?? []).map((s) => ({ ...s, watch_url: `${consoleBase}/console/${s.session_id}` }));
|
|
792
|
-
return
|
|
1045
|
+
return structuredResult({
|
|
1046
|
+
ok: true,
|
|
1047
|
+
tool: "browser_list_sessions",
|
|
1048
|
+
session_id: null,
|
|
1049
|
+
sessions,
|
|
1050
|
+
count: sessions.length
|
|
1051
|
+
});
|
|
793
1052
|
}
|
|
794
1053
|
);
|
|
795
1054
|
}
|