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
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
PACKAGE_VERSION
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-XKUDVN2E.js";
|
|
4
4
|
|
|
5
5
|
// src/mcp/browser-agent-mcp-server.ts
|
|
6
6
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
@@ -10,6 +10,18 @@ import { join as join2 } from "path";
|
|
|
10
10
|
|
|
11
11
|
// src/mcp/browser-agent-tool-schemas.ts
|
|
12
12
|
import { z } from "zod";
|
|
13
|
+
var NullableString = z.string().nullable();
|
|
14
|
+
var BrowserRawObject = z.record(z.unknown());
|
|
15
|
+
var BrowserElementOutput = z.record(z.unknown());
|
|
16
|
+
var BrowserBaseOutput = {
|
|
17
|
+
ok: z.boolean().describe("Whether the browser-agent action succeeded."),
|
|
18
|
+
tool: z.string().describe("Browser Agent MCP tool that produced this response."),
|
|
19
|
+
session_id: NullableString.describe("Browser session id when the response is scoped to a session.")
|
|
20
|
+
};
|
|
21
|
+
var BrowserReplayBaseOutput = {
|
|
22
|
+
...BrowserBaseOutput,
|
|
23
|
+
replay_id: NullableString.describe("Replay id when the response is scoped to a replay.")
|
|
24
|
+
};
|
|
13
25
|
var BrowserOpenInputSchema = {
|
|
14
26
|
label: z.string().optional().describe("Optional human label for this session, shown in the watch console."),
|
|
15
27
|
url: z.string().url().optional().describe("Optional URL to navigate to immediately after opening."),
|
|
@@ -17,7 +29,7 @@ var BrowserOpenInputSchema = {
|
|
|
17
29
|
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.")
|
|
18
30
|
};
|
|
19
31
|
var BrowserSessionInputSchema = {
|
|
20
|
-
session_id: z.string().describe("The session id returned by browser_open.")
|
|
32
|
+
session_id: z.string().describe("The session id returned by browser_open or browser_list_sessions. Use only a returned session_id; do not construct one yourself.")
|
|
21
33
|
};
|
|
22
34
|
var BrowserLocateTargetSchema = z.object({
|
|
23
35
|
name: z.string().optional().describe("Optional label for this target, echoed in the result."),
|
|
@@ -29,43 +41,43 @@ var BrowserLocateTargetSchema = z.object({
|
|
|
29
41
|
message: "target requires selector or text"
|
|
30
42
|
});
|
|
31
43
|
var BrowserLocateInputSchema = {
|
|
32
|
-
session_id: z.string().describe("The session id returned by browser_open."),
|
|
44
|
+
session_id: z.string().describe("The session id returned by browser_open or browser_list_sessions. Use only a returned session_id; do not construct one yourself."),
|
|
33
45
|
targets: z.array(BrowserLocateTargetSchema).min(1).max(20).describe("DOM targets to locate in the current viewport. Use selectors for exact elements, or text for visible text ranges.")
|
|
34
46
|
};
|
|
35
47
|
var BrowserGotoInputSchema = {
|
|
36
|
-
session_id: z.string().describe("The session id returned by browser_open."),
|
|
48
|
+
session_id: z.string().describe("The session id returned by browser_open or browser_list_sessions. Use only a returned session_id; do not construct one yourself."),
|
|
37
49
|
url: z.string().url().describe("URL to navigate the browser to.")
|
|
38
50
|
};
|
|
39
51
|
var BrowserClickInputSchema = {
|
|
40
|
-
session_id: z.string().describe("The session id returned by browser_open."),
|
|
41
|
-
x: z.number().describe("X coordinate to click, in screenshot pixels. Use the
|
|
42
|
-
y: z.number().describe("Y coordinate to click, in screenshot pixels."),
|
|
52
|
+
session_id: z.string().describe("The session id returned by browser_open or browser_list_sessions. Use only a returned session_id; do not construct one yourself."),
|
|
53
|
+
x: z.number().describe("X coordinate to click, in screenshot pixels. Use only coordinates from the latest browser_screenshot, browser_read, or browser_locate result; do not guess."),
|
|
54
|
+
y: z.number().describe("Y coordinate to click, in screenshot pixels. Use only coordinates from the latest browser_screenshot, browser_read, or browser_locate result; do not guess."),
|
|
43
55
|
button: z.enum(["left", "right", "middle"]).default("left").describe("Mouse button."),
|
|
44
56
|
num_clicks: z.number().int().min(1).max(3).optional().describe("Number of clicks, e.g. 2 for double-click.")
|
|
45
57
|
};
|
|
46
58
|
var BrowserTypeInputSchema = {
|
|
47
|
-
session_id: z.string().describe("The session id returned by browser_open."),
|
|
59
|
+
session_id: z.string().describe("The session id returned by browser_open or browser_list_sessions. Use only a returned session_id; do not construct one yourself."),
|
|
48
60
|
text: z.string().describe("Text to type at the current focus. Click a field first to focus it."),
|
|
49
61
|
delay: z.number().int().min(0).max(500).optional().describe("Optional per-keystroke delay in ms for human-like typing.")
|
|
50
62
|
};
|
|
51
63
|
var BrowserScrollInputSchema = {
|
|
52
|
-
session_id: z.string().describe("The session id returned by browser_open."),
|
|
64
|
+
session_id: z.string().describe("The session id returned by browser_open or browser_list_sessions. Use only a returned session_id; do not construct one yourself."),
|
|
53
65
|
delta_y: z.number().default(5).describe("Vertical scroll in wheel units. Positive scrolls down, negative up."),
|
|
54
66
|
delta_x: z.number().default(0).describe("Horizontal scroll in wheel units."),
|
|
55
67
|
x: z.number().optional().describe("X position to scroll at. Defaults to screen center."),
|
|
56
68
|
y: z.number().optional().describe("Y position to scroll at. Defaults to screen center.")
|
|
57
69
|
};
|
|
58
70
|
var BrowserPressInputSchema = {
|
|
59
|
-
session_id: z.string().describe("The session id returned by browser_open."),
|
|
71
|
+
session_id: z.string().describe("The session id returned by browser_open or browser_list_sessions. Use only a returned session_id; do not construct one yourself."),
|
|
60
72
|
keys: z.array(z.string()).min(1).describe('Keys or combinations to press, e.g. ["Return"], ["Ctrl+a"], ["Ctrl+Shift+Tab"].')
|
|
61
73
|
};
|
|
62
74
|
var BrowserReplayStopInputSchema = {
|
|
63
|
-
session_id: z.string().describe("The session id returned by browser_open."),
|
|
64
|
-
replay_id: z.string().describe("The replay id returned by browser_replay_start.")
|
|
75
|
+
session_id: z.string().describe("The session id returned by browser_open or browser_list_sessions. Use only a returned session_id; do not construct one yourself."),
|
|
76
|
+
replay_id: z.string().describe("The replay id returned by browser_replay_start or browser_list_replays. Use only a returned replay_id; do not construct one yourself.")
|
|
65
77
|
};
|
|
66
78
|
var BrowserReplayDownloadInputSchema = {
|
|
67
|
-
session_id: z.string().describe("The session id returned by browser_open."),
|
|
68
|
-
replay_id: z.string().describe("The replay id returned by browser_replay_start or browser_list_replays."),
|
|
79
|
+
session_id: z.string().describe("The session id returned by browser_open or browser_list_sessions. Use only a returned session_id; do not construct one yourself."),
|
|
80
|
+
replay_id: z.string().describe("The replay id returned by browser_replay_start or browser_list_replays. Use only a returned replay_id; do not construct one yourself."),
|
|
69
81
|
filename: z.string().optional().describe("Optional local MP4 filename. Defaults to a timestamped replay filename.")
|
|
70
82
|
};
|
|
71
83
|
var BrowserReplayAnnotationSchema = z.object({
|
|
@@ -87,7 +99,7 @@ var BrowserReplayAnnotationSchema = z.object({
|
|
|
87
99
|
thickness: z.number().min(1).max(24).optional().describe("Stroke thickness in pixels. Defaults to 5.")
|
|
88
100
|
});
|
|
89
101
|
var BrowserReplayMarkInputSchema = {
|
|
90
|
-
session_id: z.string().describe("The session id returned by browser_open. A replay must already be recording."),
|
|
102
|
+
session_id: z.string().describe("The session id returned by browser_open or browser_list_sessions. Use only a returned session_id; do not construct one yourself. A replay must already be recording."),
|
|
91
103
|
target: BrowserLocateTargetSchema.describe("The exact DOM element or text range to mark in the current viewport."),
|
|
92
104
|
type: z.enum(["box", "circle", "underline", "arrow"]).default("box").describe("Annotation style to generate. Labels are included on the returned annotation when label is provided."),
|
|
93
105
|
label: z.string().max(120).optional().describe("Optional callout text to render near the target."),
|
|
@@ -98,8 +110,8 @@ var BrowserReplayMarkInputSchema = {
|
|
|
98
110
|
duration_seconds: z.number().min(0.5).max(30).default(4).describe("How long the generated annotation should remain visible.")
|
|
99
111
|
};
|
|
100
112
|
var BrowserReplayAnnotateInputSchema = {
|
|
101
|
-
session_id: z.string().describe("The session id returned by browser_open."),
|
|
102
|
-
replay_id: z.string().describe("The replay id returned by browser_replay_start or browser_list_replays."),
|
|
113
|
+
session_id: z.string().describe("The session id returned by browser_open or browser_list_sessions. Use only a returned session_id; do not construct one yourself."),
|
|
114
|
+
replay_id: z.string().describe("The replay id returned by browser_replay_start or browser_list_replays. Use only a returned replay_id; do not construct one yourself."),
|
|
103
115
|
annotations: z.array(BrowserReplayAnnotationSchema).min(1).max(50).describe("Timed overlay annotations to render on the replay. Prefer annotations returned by browser_replay_mark; otherwise use exact DOM bounds from browser_locate/browser_screenshot/browser_read."),
|
|
104
116
|
filename: z.string().optional().describe("Optional output MP4 filename. Defaults to a timestamped annotated replay filename."),
|
|
105
117
|
source_width: z.number().positive().optional().describe("Width of the screenshot coordinate space used for annotations. Defaults to the replay video width."),
|
|
@@ -110,6 +122,113 @@ var BrowserReplayAnnotateInputSchema = {
|
|
|
110
122
|
var BrowserListInputSchema = {
|
|
111
123
|
include_closed: z.boolean().default(false).describe("Include closed sessions in the list.")
|
|
112
124
|
};
|
|
125
|
+
var BrowserOpenOutputSchema = {
|
|
126
|
+
ok: z.boolean(),
|
|
127
|
+
tool: z.literal("browser_open"),
|
|
128
|
+
session_id: z.string().describe("Session id returned by browser_open. Use only this exact value in later browser_* calls; do not construct one yourself."),
|
|
129
|
+
watch_url: z.string().describe("Human watch/takeover URL for this browser session."),
|
|
130
|
+
live_view_url: NullableString.describe("Raw live-view URL from the browser service when available."),
|
|
131
|
+
url: NullableString.describe("Initial URL requested by the caller, when provided."),
|
|
132
|
+
hint: z.string(),
|
|
133
|
+
raw: BrowserRawObject.optional()
|
|
134
|
+
};
|
|
135
|
+
var BrowserScreenshotOutputSchema = {
|
|
136
|
+
...BrowserBaseOutput,
|
|
137
|
+
tool: z.literal("browser_screenshot"),
|
|
138
|
+
url: NullableString,
|
|
139
|
+
title: NullableString,
|
|
140
|
+
text: z.string(),
|
|
141
|
+
elements: z.array(BrowserElementOutput),
|
|
142
|
+
screenshot: z.object({
|
|
143
|
+
mime_type: z.string(),
|
|
144
|
+
inline: z.boolean()
|
|
145
|
+
}).nullable()
|
|
146
|
+
};
|
|
147
|
+
var BrowserReadOutputSchema = {
|
|
148
|
+
...BrowserBaseOutput,
|
|
149
|
+
tool: z.literal("browser_read"),
|
|
150
|
+
url: NullableString,
|
|
151
|
+
title: NullableString,
|
|
152
|
+
text: z.string(),
|
|
153
|
+
elements: z.array(BrowserElementOutput),
|
|
154
|
+
raw: BrowserRawObject.optional()
|
|
155
|
+
};
|
|
156
|
+
var BrowserLocateOutputSchema = {
|
|
157
|
+
...BrowserBaseOutput,
|
|
158
|
+
tool: z.literal("browser_locate"),
|
|
159
|
+
url: NullableString,
|
|
160
|
+
title: NullableString,
|
|
161
|
+
viewport: BrowserRawObject.nullable(),
|
|
162
|
+
replay: BrowserRawObject.nullable(),
|
|
163
|
+
targets: z.array(BrowserRawObject),
|
|
164
|
+
raw: BrowserRawObject.optional()
|
|
165
|
+
};
|
|
166
|
+
var BrowserActionOutputSchema = {
|
|
167
|
+
...BrowserBaseOutput,
|
|
168
|
+
result: BrowserRawObject.describe("Provider action result. Check ok and follow with browser_screenshot/browser_read when page state matters."),
|
|
169
|
+
nextRecommendedTool: z.string().nullable()
|
|
170
|
+
};
|
|
171
|
+
var BrowserReplayStartOutputSchema = {
|
|
172
|
+
...BrowserReplayBaseOutput,
|
|
173
|
+
tool: z.literal("browser_replay_start"),
|
|
174
|
+
view_url: NullableString,
|
|
175
|
+
download_url: NullableString,
|
|
176
|
+
raw: BrowserRawObject.optional()
|
|
177
|
+
};
|
|
178
|
+
var BrowserReplayStopOutputSchema = {
|
|
179
|
+
...BrowserReplayBaseOutput,
|
|
180
|
+
tool: z.literal("browser_replay_stop"),
|
|
181
|
+
view_url: NullableString,
|
|
182
|
+
download_url: NullableString,
|
|
183
|
+
raw: BrowserRawObject.optional()
|
|
184
|
+
};
|
|
185
|
+
var BrowserListReplaysOutputSchema = {
|
|
186
|
+
...BrowserBaseOutput,
|
|
187
|
+
tool: z.literal("browser_list_replays"),
|
|
188
|
+
replays: z.array(BrowserRawObject),
|
|
189
|
+
count: z.number().int().min(0)
|
|
190
|
+
};
|
|
191
|
+
var BrowserReplayDownloadOutputSchema = {
|
|
192
|
+
...BrowserReplayBaseOutput,
|
|
193
|
+
tool: z.literal("browser_replay_download"),
|
|
194
|
+
file_path: NullableString,
|
|
195
|
+
bytes: z.number().int().min(0).nullable(),
|
|
196
|
+
mime_type: NullableString,
|
|
197
|
+
download_url: NullableString
|
|
198
|
+
};
|
|
199
|
+
var BrowserReplayMarkOutputSchema = {
|
|
200
|
+
...BrowserReplayBaseOutput,
|
|
201
|
+
tool: z.literal("browser_replay_mark"),
|
|
202
|
+
annotation: BrowserRawObject,
|
|
203
|
+
source_width: z.number().nullable(),
|
|
204
|
+
source_height: z.number().nullable(),
|
|
205
|
+
target: BrowserRawObject.nullable(),
|
|
206
|
+
hint: z.string()
|
|
207
|
+
};
|
|
208
|
+
var BrowserReplayAnnotateOutputSchema = {
|
|
209
|
+
...BrowserReplayBaseOutput,
|
|
210
|
+
tool: z.literal("browser_replay_annotate"),
|
|
211
|
+
source_file_path: NullableString,
|
|
212
|
+
annotated_file_path: NullableString,
|
|
213
|
+
bytes: z.number().int().min(0).nullable(),
|
|
214
|
+
width: z.number().int().min(0).nullable(),
|
|
215
|
+
height: z.number().int().min(0).nullable(),
|
|
216
|
+
annotation_count: z.number().int().min(0).nullable(),
|
|
217
|
+
mime_type: NullableString
|
|
218
|
+
};
|
|
219
|
+
var BrowserCloseOutputSchema = {
|
|
220
|
+
...BrowserBaseOutput,
|
|
221
|
+
tool: z.literal("browser_close"),
|
|
222
|
+
closed: z.boolean(),
|
|
223
|
+
raw: BrowserRawObject.optional()
|
|
224
|
+
};
|
|
225
|
+
var BrowserListSessionsOutputSchema = {
|
|
226
|
+
ok: z.boolean(),
|
|
227
|
+
tool: z.literal("browser_list_sessions"),
|
|
228
|
+
session_id: z.null(),
|
|
229
|
+
sessions: z.array(BrowserRawObject),
|
|
230
|
+
count: z.number().int().min(0)
|
|
231
|
+
};
|
|
113
232
|
|
|
114
233
|
// src/mcp/replay-annotator.ts
|
|
115
234
|
import { execFile } from "child_process";
|
|
@@ -372,8 +491,40 @@ async function annotateReplayVideo(inputFilePath, outputFilePath, options) {
|
|
|
372
491
|
}
|
|
373
492
|
|
|
374
493
|
// src/mcp/browser-agent-mcp-server.ts
|
|
375
|
-
function
|
|
376
|
-
return {
|
|
494
|
+
function structuredResult(value, isError = false) {
|
|
495
|
+
return {
|
|
496
|
+
content: [{ type: "text", text: JSON.stringify(value) }],
|
|
497
|
+
structuredContent: value,
|
|
498
|
+
isError
|
|
499
|
+
};
|
|
500
|
+
}
|
|
501
|
+
function errorMessage(value) {
|
|
502
|
+
if (value && typeof value === "object") {
|
|
503
|
+
const data = value;
|
|
504
|
+
if (typeof data.error === "string") return data.error;
|
|
505
|
+
if (typeof data.message === "string") return data.message;
|
|
506
|
+
}
|
|
507
|
+
return typeof value === "string" ? value : "Browser Agent request failed";
|
|
508
|
+
}
|
|
509
|
+
function errorResult(tool, value, sessionId = null, replayId = null) {
|
|
510
|
+
return structuredResult({
|
|
511
|
+
ok: false,
|
|
512
|
+
tool,
|
|
513
|
+
session_id: sessionId,
|
|
514
|
+
...replayId !== null ? { replay_id: replayId } : {},
|
|
515
|
+
error: errorMessage(value),
|
|
516
|
+
raw: value && typeof value === "object" ? value : { value }
|
|
517
|
+
}, true);
|
|
518
|
+
}
|
|
519
|
+
function actionResult(tool, sessionId, ok, data, nextRecommendedTool = "browser_screenshot") {
|
|
520
|
+
if (!ok) return errorResult(tool, data, sessionId);
|
|
521
|
+
return structuredResult({
|
|
522
|
+
ok: true,
|
|
523
|
+
tool,
|
|
524
|
+
session_id: sessionId,
|
|
525
|
+
result: data && typeof data === "object" ? data : { value: data },
|
|
526
|
+
nextRecommendedTool
|
|
527
|
+
});
|
|
377
528
|
}
|
|
378
529
|
function outputBaseDir() {
|
|
379
530
|
return process.env.MCP_SCRAPER_OUTPUT_DIR?.trim() || join2(homedir(), "Downloads", "mcp-scraper");
|
|
@@ -478,6 +629,7 @@ function registerBrowserAgentMcpTools(server, opts) {
|
|
|
478
629
|
title: "Open Browser Session",
|
|
479
630
|
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.",
|
|
480
631
|
inputSchema: BrowserOpenInputSchema,
|
|
632
|
+
outputSchema: BrowserOpenOutputSchema,
|
|
481
633
|
annotations: annotations("Open Browser Session")
|
|
482
634
|
},
|
|
483
635
|
async (input) => {
|
|
@@ -486,16 +638,20 @@ function registerBrowserAgentMcpTools(server, opts) {
|
|
|
486
638
|
profile: input.profile,
|
|
487
639
|
timeout_seconds: input.timeout_seconds
|
|
488
640
|
});
|
|
489
|
-
if (!open.ok) return
|
|
641
|
+
if (!open.ok) return errorResult("browser_open", open.data);
|
|
490
642
|
const session = open.data;
|
|
491
643
|
if (input.url) {
|
|
492
644
|
await req("POST", `/agent/sessions/${session.session_id}/goto`, { url: input.url });
|
|
493
645
|
}
|
|
494
|
-
return
|
|
646
|
+
return structuredResult({
|
|
647
|
+
ok: true,
|
|
648
|
+
tool: "browser_open",
|
|
495
649
|
session_id: session.session_id,
|
|
496
650
|
watch_url: `${consoleBase}/console/${session.session_id}`,
|
|
497
651
|
live_view_url: session.live_view_url ?? null,
|
|
498
|
-
|
|
652
|
+
url: input.url ?? null,
|
|
653
|
+
hint: "Call browser_screenshot to see the page. Click by the x,y of an element from the snapshot.",
|
|
654
|
+
raw: session
|
|
499
655
|
});
|
|
500
656
|
}
|
|
501
657
|
);
|
|
@@ -505,19 +661,30 @@ function registerBrowserAgentMcpTools(server, opts) {
|
|
|
505
661
|
title: "See Page (Screenshot + Elements)",
|
|
506
662
|
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.",
|
|
507
663
|
inputSchema: BrowserSessionInputSchema,
|
|
664
|
+
outputSchema: BrowserScreenshotOutputSchema,
|
|
508
665
|
annotations: annotations("See Page", true)
|
|
509
666
|
},
|
|
510
667
|
async (input) => {
|
|
511
668
|
const res = await req("POST", `/agent/sessions/${input.session_id}/screenshot`);
|
|
512
|
-
if (!res.ok) return
|
|
669
|
+
if (!res.ok) return errorResult("browser_screenshot", res.data, input.session_id);
|
|
513
670
|
const { image_base64, mime_type, url, title, elements, text } = res.data;
|
|
514
671
|
const content = [];
|
|
515
672
|
if (image_base64) content.push({ type: "image", data: image_base64, mimeType: mime_type ?? "image/png" });
|
|
673
|
+
const structured = {
|
|
674
|
+
ok: true,
|
|
675
|
+
tool: "browser_screenshot",
|
|
676
|
+
session_id: input.session_id,
|
|
677
|
+
url: url ?? null,
|
|
678
|
+
title: title ?? null,
|
|
679
|
+
text: typeof text === "string" ? text : "",
|
|
680
|
+
elements: Array.isArray(elements) ? elements : [],
|
|
681
|
+
screenshot: image_base64 ? { mime_type: mime_type ?? "image/png", inline: true } : null
|
|
682
|
+
};
|
|
516
683
|
content.push({
|
|
517
684
|
type: "text",
|
|
518
|
-
text: JSON.stringify(
|
|
685
|
+
text: JSON.stringify(structured)
|
|
519
686
|
});
|
|
520
|
-
return { content };
|
|
687
|
+
return { content, structuredContent: structured };
|
|
521
688
|
}
|
|
522
689
|
);
|
|
523
690
|
server.registerTool(
|
|
@@ -526,11 +693,22 @@ function registerBrowserAgentMcpTools(server, opts) {
|
|
|
526
693
|
title: "Read Page Text + Elements",
|
|
527
694
|
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.",
|
|
528
695
|
inputSchema: BrowserSessionInputSchema,
|
|
696
|
+
outputSchema: BrowserReadOutputSchema,
|
|
529
697
|
annotations: annotations("Read Page", true)
|
|
530
698
|
},
|
|
531
699
|
async (input) => {
|
|
532
700
|
const res = await req("POST", `/agent/sessions/${input.session_id}/read`);
|
|
533
|
-
return
|
|
701
|
+
if (!res.ok) return errorResult("browser_read", res.data, input.session_id);
|
|
702
|
+
return structuredResult({
|
|
703
|
+
ok: true,
|
|
704
|
+
tool: "browser_read",
|
|
705
|
+
session_id: input.session_id,
|
|
706
|
+
url: res.data?.url ?? null,
|
|
707
|
+
title: res.data?.title ?? null,
|
|
708
|
+
text: typeof res.data?.text === "string" ? res.data.text : "",
|
|
709
|
+
elements: Array.isArray(res.data?.elements) ? res.data.elements : [],
|
|
710
|
+
raw: res.data
|
|
711
|
+
});
|
|
534
712
|
}
|
|
535
713
|
);
|
|
536
714
|
server.registerTool(
|
|
@@ -539,32 +717,46 @@ function registerBrowserAgentMcpTools(server, opts) {
|
|
|
539
717
|
title: "Locate DOM Targets",
|
|
540
718
|
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.",
|
|
541
719
|
inputSchema: BrowserLocateInputSchema,
|
|
720
|
+
outputSchema: BrowserLocateOutputSchema,
|
|
542
721
|
annotations: annotations("Locate DOM Targets", true)
|
|
543
722
|
},
|
|
544
723
|
async (input) => {
|
|
545
724
|
const res = await req("POST", `/agent/sessions/${input.session_id}/locate`, { targets: input.targets });
|
|
546
|
-
return
|
|
725
|
+
if (!res.ok) return errorResult("browser_locate", res.data, input.session_id);
|
|
726
|
+
return structuredResult({
|
|
727
|
+
ok: true,
|
|
728
|
+
tool: "browser_locate",
|
|
729
|
+
session_id: input.session_id,
|
|
730
|
+
url: res.data?.url ?? null,
|
|
731
|
+
title: res.data?.title ?? null,
|
|
732
|
+
viewport: res.data?.viewport ?? null,
|
|
733
|
+
replay: res.data?.replay ?? null,
|
|
734
|
+
targets: Array.isArray(res.data?.targets) ? res.data.targets : [],
|
|
735
|
+
raw: res.data
|
|
736
|
+
});
|
|
547
737
|
}
|
|
548
738
|
);
|
|
549
739
|
server.registerTool(
|
|
550
740
|
"browser_goto",
|
|
551
741
|
{
|
|
552
742
|
title: "Navigate To URL",
|
|
553
|
-
description: "Navigate
|
|
743
|
+
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.",
|
|
554
744
|
inputSchema: BrowserGotoInputSchema,
|
|
745
|
+
outputSchema: BrowserActionOutputSchema,
|
|
555
746
|
annotations: annotations("Navigate To URL")
|
|
556
747
|
},
|
|
557
748
|
async (input) => {
|
|
558
749
|
const res = await req("POST", `/agent/sessions/${input.session_id}/goto`, { url: input.url });
|
|
559
|
-
return
|
|
750
|
+
return actionResult("browser_goto", input.session_id, res.ok, res.data, "browser_screenshot");
|
|
560
751
|
}
|
|
561
752
|
);
|
|
562
753
|
server.registerTool(
|
|
563
754
|
"browser_click",
|
|
564
755
|
{
|
|
565
756
|
title: "Click",
|
|
566
|
-
description: "Click
|
|
757
|
+
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.",
|
|
567
758
|
inputSchema: BrowserClickInputSchema,
|
|
759
|
+
outputSchema: BrowserActionOutputSchema,
|
|
568
760
|
annotations: annotations("Click")
|
|
569
761
|
},
|
|
570
762
|
async (input) => {
|
|
@@ -574,28 +766,30 @@ function registerBrowserAgentMcpTools(server, opts) {
|
|
|
574
766
|
button: input.button,
|
|
575
767
|
num_clicks: input.num_clicks
|
|
576
768
|
});
|
|
577
|
-
return
|
|
769
|
+
return actionResult("browser_click", input.session_id, res.ok, res.data, "browser_screenshot");
|
|
578
770
|
}
|
|
579
771
|
);
|
|
580
772
|
server.registerTool(
|
|
581
773
|
"browser_type",
|
|
582
774
|
{
|
|
583
775
|
title: "Type Text",
|
|
584
|
-
description: 'Type text
|
|
776
|
+
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.',
|
|
585
777
|
inputSchema: BrowserTypeInputSchema,
|
|
778
|
+
outputSchema: BrowserActionOutputSchema,
|
|
586
779
|
annotations: annotations("Type Text")
|
|
587
780
|
},
|
|
588
781
|
async (input) => {
|
|
589
782
|
const res = await req("POST", `/agent/sessions/${input.session_id}/type`, { text: input.text, delay: input.delay });
|
|
590
|
-
return
|
|
783
|
+
return actionResult("browser_type", input.session_id, res.ok, res.data, "browser_screenshot");
|
|
591
784
|
}
|
|
592
785
|
);
|
|
593
786
|
server.registerTool(
|
|
594
787
|
"browser_scroll",
|
|
595
788
|
{
|
|
596
789
|
title: "Scroll",
|
|
597
|
-
description: "Scroll the page. Positive delta_y scrolls down. Follow with browser_screenshot to
|
|
790
|
+
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.",
|
|
598
791
|
inputSchema: BrowserScrollInputSchema,
|
|
792
|
+
outputSchema: BrowserActionOutputSchema,
|
|
599
793
|
annotations: annotations("Scroll")
|
|
600
794
|
},
|
|
601
795
|
async (input) => {
|
|
@@ -605,20 +799,21 @@ function registerBrowserAgentMcpTools(server, opts) {
|
|
|
605
799
|
x: input.x,
|
|
606
800
|
y: input.y
|
|
607
801
|
});
|
|
608
|
-
return
|
|
802
|
+
return actionResult("browser_scroll", input.session_id, res.ok, res.data, "browser_screenshot");
|
|
609
803
|
}
|
|
610
804
|
);
|
|
611
805
|
server.registerTool(
|
|
612
806
|
"browser_press",
|
|
613
807
|
{
|
|
614
808
|
title: "Press Keys",
|
|
615
|
-
description: 'Press keys or combinations,
|
|
809
|
+
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.',
|
|
616
810
|
inputSchema: BrowserPressInputSchema,
|
|
811
|
+
outputSchema: BrowserActionOutputSchema,
|
|
617
812
|
annotations: annotations("Press Keys")
|
|
618
813
|
},
|
|
619
814
|
async (input) => {
|
|
620
815
|
const res = await req("POST", `/agent/sessions/${input.session_id}/press`, { keys: input.keys });
|
|
621
|
-
return
|
|
816
|
+
return actionResult("browser_press", input.session_id, res.ok, res.data, "browser_screenshot");
|
|
622
817
|
}
|
|
623
818
|
);
|
|
624
819
|
server.registerTool(
|
|
@@ -627,11 +822,21 @@ function registerBrowserAgentMcpTools(server, opts) {
|
|
|
627
822
|
title: "Start Recording",
|
|
628
823
|
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.",
|
|
629
824
|
inputSchema: BrowserSessionInputSchema,
|
|
825
|
+
outputSchema: BrowserReplayStartOutputSchema,
|
|
630
826
|
annotations: annotations("Start Recording")
|
|
631
827
|
},
|
|
632
828
|
async (input) => {
|
|
633
829
|
const res = await req("POST", `/agent/sessions/${input.session_id}/replay/start`);
|
|
634
|
-
return
|
|
830
|
+
if (!res.ok) return errorResult("browser_replay_start", res.data, input.session_id);
|
|
831
|
+
return structuredResult({
|
|
832
|
+
ok: true,
|
|
833
|
+
tool: "browser_replay_start",
|
|
834
|
+
session_id: input.session_id,
|
|
835
|
+
replay_id: res.data?.replay_id ?? res.data?.replayId ?? null,
|
|
836
|
+
view_url: res.data?.view_url ?? res.data?.viewUrl ?? null,
|
|
837
|
+
download_url: res.data?.download_url ?? res.data?.downloadUrl ?? null,
|
|
838
|
+
raw: res.data
|
|
839
|
+
});
|
|
635
840
|
}
|
|
636
841
|
);
|
|
637
842
|
server.registerTool(
|
|
@@ -640,11 +845,21 @@ function registerBrowserAgentMcpTools(server, opts) {
|
|
|
640
845
|
title: "Stop Recording",
|
|
641
846
|
description: "Stop a replay recording and expose the final view_url and download_url. Use browser_replay_download to save the MP4 locally.",
|
|
642
847
|
inputSchema: BrowserReplayStopInputSchema,
|
|
848
|
+
outputSchema: BrowserReplayStopOutputSchema,
|
|
643
849
|
annotations: annotations("Stop Recording")
|
|
644
850
|
},
|
|
645
851
|
async (input) => {
|
|
646
852
|
const res = await req("POST", `/agent/sessions/${input.session_id}/replay/stop`, { replay_id: input.replay_id });
|
|
647
|
-
return
|
|
853
|
+
if (!res.ok) return errorResult("browser_replay_stop", res.data, input.session_id, input.replay_id);
|
|
854
|
+
return structuredResult({
|
|
855
|
+
ok: true,
|
|
856
|
+
tool: "browser_replay_stop",
|
|
857
|
+
session_id: input.session_id,
|
|
858
|
+
replay_id: res.data?.replay_id ?? res.data?.replayId ?? input.replay_id,
|
|
859
|
+
view_url: res.data?.view_url ?? res.data?.viewUrl ?? null,
|
|
860
|
+
download_url: res.data?.download_url ?? res.data?.downloadUrl ?? null,
|
|
861
|
+
raw: res.data
|
|
862
|
+
});
|
|
648
863
|
}
|
|
649
864
|
);
|
|
650
865
|
server.registerTool(
|
|
@@ -653,11 +868,20 @@ function registerBrowserAgentMcpTools(server, opts) {
|
|
|
653
868
|
title: "List Replay Videos",
|
|
654
869
|
description: "List replay recordings for a browser session, including final view_url and authenticated download_url values when available.",
|
|
655
870
|
inputSchema: BrowserSessionInputSchema,
|
|
871
|
+
outputSchema: BrowserListReplaysOutputSchema,
|
|
656
872
|
annotations: annotations("List Replay Videos", true)
|
|
657
873
|
},
|
|
658
874
|
async (input) => {
|
|
659
875
|
const res = await req("GET", `/agent/sessions/${input.session_id}/replays`);
|
|
660
|
-
return
|
|
876
|
+
if (!res.ok) return errorResult("browser_list_replays", res.data, input.session_id);
|
|
877
|
+
const replays = Array.isArray(res.data?.replays) ? res.data.replays : [];
|
|
878
|
+
return structuredResult({
|
|
879
|
+
ok: true,
|
|
880
|
+
tool: "browser_list_replays",
|
|
881
|
+
session_id: input.session_id,
|
|
882
|
+
replays,
|
|
883
|
+
count: replays.length
|
|
884
|
+
});
|
|
661
885
|
}
|
|
662
886
|
);
|
|
663
887
|
server.registerTool(
|
|
@@ -666,11 +890,22 @@ function registerBrowserAgentMcpTools(server, opts) {
|
|
|
666
890
|
title: "Download Replay MP4",
|
|
667
891
|
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.",
|
|
668
892
|
inputSchema: BrowserReplayDownloadInputSchema,
|
|
669
|
-
|
|
893
|
+
outputSchema: BrowserReplayDownloadOutputSchema,
|
|
894
|
+
annotations: annotations("Download Replay MP4")
|
|
670
895
|
},
|
|
671
896
|
async (input) => {
|
|
672
897
|
const res = await downloadReplay(input.session_id, input.replay_id, input.filename);
|
|
673
|
-
return
|
|
898
|
+
if (!res.ok) return errorResult("browser_replay_download", res.data, input.session_id, input.replay_id);
|
|
899
|
+
return structuredResult({
|
|
900
|
+
ok: true,
|
|
901
|
+
tool: "browser_replay_download",
|
|
902
|
+
session_id: input.session_id,
|
|
903
|
+
replay_id: input.replay_id,
|
|
904
|
+
file_path: res.data?.file_path ?? null,
|
|
905
|
+
bytes: typeof res.data?.bytes === "number" ? res.data.bytes : null,
|
|
906
|
+
mime_type: res.data?.mime_type ?? null,
|
|
907
|
+
download_url: res.data?.download_url ?? null
|
|
908
|
+
});
|
|
674
909
|
}
|
|
675
910
|
);
|
|
676
911
|
server.registerTool(
|
|
@@ -679,19 +914,20 @@ function registerBrowserAgentMcpTools(server, opts) {
|
|
|
679
914
|
title: "Mark Replay Annotation",
|
|
680
915
|
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.",
|
|
681
916
|
inputSchema: BrowserReplayMarkInputSchema,
|
|
917
|
+
outputSchema: BrowserReplayMarkOutputSchema,
|
|
682
918
|
annotations: annotations("Mark Replay Annotation", true)
|
|
683
919
|
},
|
|
684
920
|
async (input) => {
|
|
685
921
|
const res = await req("POST", `/agent/sessions/${input.session_id}/locate`, { targets: [input.target] });
|
|
686
|
-
if (!res.ok) return
|
|
922
|
+
if (!res.ok) return errorResult("browser_replay_mark", res.data, input.session_id);
|
|
687
923
|
const target = res.data?.targets?.[0];
|
|
688
924
|
const element = target?.element;
|
|
689
925
|
const elapsed = res.data?.replay?.replay_elapsed_seconds;
|
|
690
926
|
if (!target?.found || !element) {
|
|
691
|
-
return
|
|
927
|
+
return errorResult("browser_replay_mark", { error: target?.error ?? "target not found in current viewport", target }, input.session_id);
|
|
692
928
|
}
|
|
693
929
|
if (!finiteNumber2(elapsed)) {
|
|
694
|
-
return
|
|
930
|
+
return errorResult("browser_replay_mark", { error: "no active replay clock found; call browser_replay_start before browser_replay_mark" }, input.session_id);
|
|
695
931
|
}
|
|
696
932
|
const padded = expandElementBounds(element, res.data?.viewport, input.padding ?? 8);
|
|
697
933
|
const start = Math.max(0, elapsed + (input.start_offset_seconds ?? -0.25));
|
|
@@ -708,7 +944,11 @@ function registerBrowserAgentMcpTools(server, opts) {
|
|
|
708
944
|
...input.color ? { color: input.color } : {},
|
|
709
945
|
...input.thickness ? { thickness: input.thickness } : {}
|
|
710
946
|
};
|
|
711
|
-
return
|
|
947
|
+
return structuredResult({
|
|
948
|
+
ok: true,
|
|
949
|
+
tool: "browser_replay_mark",
|
|
950
|
+
session_id: input.session_id,
|
|
951
|
+
replay_id: res.data?.replay?.replay_id ?? res.data?.replay?.replayId ?? null,
|
|
712
952
|
annotation,
|
|
713
953
|
source_width: padded.sourceWidth,
|
|
714
954
|
source_height: padded.sourceHeight,
|
|
@@ -724,12 +964,13 @@ function registerBrowserAgentMcpTools(server, opts) {
|
|
|
724
964
|
title: "Annotate Replay MP4",
|
|
725
965
|
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.",
|
|
726
966
|
inputSchema: BrowserReplayAnnotateInputSchema,
|
|
727
|
-
|
|
967
|
+
outputSchema: BrowserReplayAnnotateOutputSchema,
|
|
968
|
+
annotations: annotations("Annotate Replay MP4")
|
|
728
969
|
},
|
|
729
970
|
async (input) => {
|
|
730
971
|
const sourceName = input.filename ? `${input.filename}-source` : void 0;
|
|
731
972
|
const downloaded = await downloadReplay(input.session_id, input.replay_id, sourceName);
|
|
732
|
-
if (!downloaded.ok) return
|
|
973
|
+
if (!downloaded.ok) return errorResult("browser_replay_annotate", downloaded.data, input.session_id, input.replay_id);
|
|
733
974
|
try {
|
|
734
975
|
const sourcePath = String(downloaded.data.file_path);
|
|
735
976
|
const outputPath = annotatedReplayFilePath(input.session_id, input.replay_id, input.filename);
|
|
@@ -741,7 +982,10 @@ function registerBrowserAgentMcpTools(server, opts) {
|
|
|
741
982
|
sourceLeftOffset: input.source_left_offset,
|
|
742
983
|
sourceTopOffset: input.source_top_offset
|
|
743
984
|
});
|
|
744
|
-
return
|
|
985
|
+
return structuredResult({
|
|
986
|
+
ok: true,
|
|
987
|
+
tool: "browser_replay_annotate",
|
|
988
|
+
session_id: input.session_id,
|
|
745
989
|
replay_id: input.replay_id,
|
|
746
990
|
source_file_path: sourcePath,
|
|
747
991
|
annotated_file_path: result.filePath,
|
|
@@ -752,7 +996,7 @@ function registerBrowserAgentMcpTools(server, opts) {
|
|
|
752
996
|
mime_type: "video/mp4"
|
|
753
997
|
});
|
|
754
998
|
} catch (err) {
|
|
755
|
-
return
|
|
999
|
+
return errorResult("browser_replay_annotate", { error: err instanceof Error ? err.message : String(err) }, input.session_id, input.replay_id);
|
|
756
1000
|
}
|
|
757
1001
|
}
|
|
758
1002
|
);
|
|
@@ -760,28 +1004,43 @@ function registerBrowserAgentMcpTools(server, opts) {
|
|
|
760
1004
|
"browser_close",
|
|
761
1005
|
{
|
|
762
1006
|
title: "Close Browser Session",
|
|
763
|
-
description: "Close and release
|
|
1007
|
+
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.",
|
|
764
1008
|
inputSchema: BrowserSessionInputSchema,
|
|
1009
|
+
outputSchema: BrowserCloseOutputSchema,
|
|
765
1010
|
annotations: { title: "Close Browser Session", readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: false }
|
|
766
1011
|
},
|
|
767
1012
|
async (input) => {
|
|
768
1013
|
const res = await req("DELETE", `/agent/sessions/${input.session_id}`);
|
|
769
|
-
return
|
|
1014
|
+
if (!res.ok) return errorResult("browser_close", res.data, input.session_id);
|
|
1015
|
+
return structuredResult({
|
|
1016
|
+
ok: true,
|
|
1017
|
+
tool: "browser_close",
|
|
1018
|
+
session_id: input.session_id,
|
|
1019
|
+
closed: true,
|
|
1020
|
+
raw: res.data
|
|
1021
|
+
});
|
|
770
1022
|
}
|
|
771
1023
|
);
|
|
772
1024
|
server.registerTool(
|
|
773
1025
|
"browser_list_sessions",
|
|
774
1026
|
{
|
|
775
1027
|
title: "List Browser Sessions",
|
|
776
|
-
description: "List
|
|
1028
|
+
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.",
|
|
777
1029
|
inputSchema: BrowserListInputSchema,
|
|
1030
|
+
outputSchema: BrowserListSessionsOutputSchema,
|
|
778
1031
|
annotations: annotations("List Browser Sessions", true)
|
|
779
1032
|
},
|
|
780
1033
|
async (input) => {
|
|
781
1034
|
const res = await req("GET", `/agent/sessions${input.include_closed ? "?all=1" : ""}`);
|
|
782
|
-
if (!res.ok) return
|
|
1035
|
+
if (!res.ok) return errorResult("browser_list_sessions", res.data);
|
|
783
1036
|
const sessions = (res.data.sessions ?? []).map((s) => ({ ...s, watch_url: `${consoleBase}/console/${s.session_id}` }));
|
|
784
|
-
return
|
|
1037
|
+
return structuredResult({
|
|
1038
|
+
ok: true,
|
|
1039
|
+
tool: "browser_list_sessions",
|
|
1040
|
+
session_id: null,
|
|
1041
|
+
sessions,
|
|
1042
|
+
count: sessions.length
|
|
1043
|
+
});
|
|
785
1044
|
}
|
|
786
1045
|
);
|
|
787
1046
|
}
|
|
@@ -790,4 +1049,4 @@ export {
|
|
|
790
1049
|
buildBrowserAgentMcpServer,
|
|
791
1050
|
registerBrowserAgentMcpTools
|
|
792
1051
|
};
|
|
793
|
-
//# sourceMappingURL=chunk-
|
|
1052
|
+
//# sourceMappingURL=chunk-L4OWOUGR.js.map
|