@ejazullah/browser-mcp 0.0.65 → 0.0.67
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/lib/config.js +5 -0
- package/lib/program.js +2 -0
- package/lib/response.js +42 -5
- package/lib/tools/snapshot.js +4 -2
- package/package.json +1 -1
package/lib/config.js
CHANGED
|
@@ -134,6 +134,8 @@ export function configFromCLIOptions(cliOptions) {
|
|
|
134
134
|
saveTrace: cliOptions.saveTrace,
|
|
135
135
|
outputDir: cliOptions.outputDir,
|
|
136
136
|
imageResponses: cliOptions.imageResponses,
|
|
137
|
+
maxResponseChars: cliOptions.maxResponseChars ? parseInt(cliOptions.maxResponseChars, 10) : undefined,
|
|
138
|
+
actionSnapshots: cliOptions.actionSnapshots,
|
|
137
139
|
mongodb: {
|
|
138
140
|
url: cliOptions.mongodbUrl,
|
|
139
141
|
dbName: cliOptions.mongodbDb,
|
|
@@ -166,6 +168,9 @@ function configFromEnv() {
|
|
|
166
168
|
options.isolated = envToBoolean(process.env.PLAYWRIGHT_MCP_ISOLATED);
|
|
167
169
|
if (process.env.PLAYWRIGHT_MCP_IMAGE_RESPONSES === 'omit')
|
|
168
170
|
options.imageResponses = 'omit';
|
|
171
|
+
options.maxResponseChars = envToString(process.env.PLAYWRIGHT_MCP_MAX_RESPONSE_CHARS);
|
|
172
|
+
if (process.env.PLAYWRIGHT_MCP_ACTION_SNAPSHOTS === 'false' || process.env.PLAYWRIGHT_MCP_ACTION_SNAPSHOTS === '0')
|
|
173
|
+
options.actionSnapshots = false;
|
|
169
174
|
options.sandbox = envToBoolean(process.env.PLAYWRIGHT_MCP_SANDBOX);
|
|
170
175
|
options.outputDir = envToString(process.env.PLAYWRIGHT_MCP_OUTPUT_DIR);
|
|
171
176
|
options.port = envToNumber(process.env.PLAYWRIGHT_MCP_PORT);
|
package/lib/program.js
CHANGED
|
@@ -65,6 +65,8 @@ program
|
|
|
65
65
|
.option('--ignore-https-errors', 'ignore https errors')
|
|
66
66
|
.option('--isolated', 'keep the browser profile in memory, do not save it to disk.')
|
|
67
67
|
.option('--image-responses <mode>', 'whether to send image responses to the client. Can be "allow" or "omit", Defaults to "allow".')
|
|
68
|
+
.option('--max-response-chars <number>', 'Maximum characters to send in a single response. Default is 40000.')
|
|
69
|
+
.option('--no-action-snapshots', 'disable automatic snapshots after tool actions like click and scroll.')
|
|
68
70
|
.option('--no-sandbox', 'disable the sandbox for all process types that are normally sandboxed.')
|
|
69
71
|
.option('--output-dir <path>', 'path to the directory for output files.')
|
|
70
72
|
.option('--port <port>', 'port to listen on for SSE transport.')
|
package/lib/response.js
CHANGED
|
@@ -20,6 +20,7 @@ export class Response {
|
|
|
20
20
|
_images = [];
|
|
21
21
|
_context;
|
|
22
22
|
_includeSnapshot = false;
|
|
23
|
+
_snapshotOffsetLine = 0;
|
|
23
24
|
_includeTabs = false;
|
|
24
25
|
_tabSnapshot;
|
|
25
26
|
_elementInteraction;
|
|
@@ -56,8 +57,14 @@ export class Response {
|
|
|
56
57
|
images() {
|
|
57
58
|
return this._images;
|
|
58
59
|
}
|
|
59
|
-
setIncludeSnapshot() {
|
|
60
|
+
setIncludeSnapshot(offsetLine) {
|
|
61
|
+
if (this._context.config.actionSnapshots === false && this.toolName !== 'browser_snapshot') {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
60
64
|
this._includeSnapshot = true;
|
|
65
|
+
if (offsetLine !== undefined) {
|
|
66
|
+
this._snapshotOffsetLine = offsetLine;
|
|
67
|
+
}
|
|
61
68
|
}
|
|
62
69
|
setIncludeTabs() {
|
|
63
70
|
this._includeTabs = true;
|
|
@@ -104,12 +111,20 @@ ${this._code.join('\n')}
|
|
|
104
111
|
response.push('');
|
|
105
112
|
}
|
|
106
113
|
else if (this._tabSnapshot) {
|
|
107
|
-
response.
|
|
114
|
+
const currentLength = response.join('\n').length;
|
|
115
|
+
const maxChars = this._context.config.maxResponseChars ?? 40000;
|
|
116
|
+
const remainingBudget = Math.max(1000, maxChars - currentLength);
|
|
117
|
+
response.push(renderTabSnapshot(this._tabSnapshot, remainingBudget, this._snapshotOffsetLine));
|
|
108
118
|
response.push('');
|
|
109
119
|
}
|
|
110
120
|
// Main response part
|
|
121
|
+
let responseText = response.join('\n');
|
|
122
|
+
const maxChars = this._context.config.maxResponseChars ?? 40000;
|
|
123
|
+
if (responseText.length > maxChars) {
|
|
124
|
+
responseText = responseText.slice(0, maxChars - 100) + '\n\n... [Response truncated due to length limits]';
|
|
125
|
+
}
|
|
111
126
|
const content = [
|
|
112
|
-
{ type: 'text', text:
|
|
127
|
+
{ type: 'text', text: responseText },
|
|
113
128
|
];
|
|
114
129
|
// Image attachments.
|
|
115
130
|
if (this._context.config.imageResponses !== 'omit') {
|
|
@@ -119,7 +134,7 @@ ${this._code.join('\n')}
|
|
|
119
134
|
return { content, isError: this._isError };
|
|
120
135
|
}
|
|
121
136
|
}
|
|
122
|
-
function renderTabSnapshot(tabSnapshot) {
|
|
137
|
+
function renderTabSnapshot(tabSnapshot, maxLength = Infinity, offsetLine = 0) {
|
|
123
138
|
const lines = [];
|
|
124
139
|
if (tabSnapshot.consoleMessages.length) {
|
|
125
140
|
lines.push(`### New console messages`);
|
|
@@ -141,8 +156,30 @@ function renderTabSnapshot(tabSnapshot) {
|
|
|
141
156
|
lines.push(`- Page URL: ${tabSnapshot.url}`);
|
|
142
157
|
lines.push(`- Page Title: ${tabSnapshot.title}`);
|
|
143
158
|
lines.push(`- Page Snapshot:`);
|
|
159
|
+
const prefix = '```yaml\n';
|
|
160
|
+
const suffix = '\n```';
|
|
161
|
+
const currentLength = lines.join('\n').length + prefix.length + suffix.length;
|
|
162
|
+
const budget = maxLength - currentLength;
|
|
163
|
+
let ariaSnapshotLines = tabSnapshot.ariaSnapshot.split('\n');
|
|
164
|
+
if (offsetLine > 0) {
|
|
165
|
+
ariaSnapshotLines = ariaSnapshotLines.slice(offsetLine);
|
|
166
|
+
}
|
|
167
|
+
let includedLines = 0;
|
|
168
|
+
let currentStringLength = 0;
|
|
169
|
+
let truncatedAriaSnapshot = '';
|
|
170
|
+
for (const line of ariaSnapshotLines) {
|
|
171
|
+
if (currentStringLength + line.length + 1 > budget)
|
|
172
|
+
break;
|
|
173
|
+
truncatedAriaSnapshot += line + '\n';
|
|
174
|
+
currentStringLength += line.length + 1;
|
|
175
|
+
includedLines++;
|
|
176
|
+
}
|
|
177
|
+
if (includedLines < ariaSnapshotLines.length) {
|
|
178
|
+
const nextOffset = offsetLine + includedLines;
|
|
179
|
+
truncatedAriaSnapshot += `\n... [ARIA Snapshot truncated due to length limits. To see more, call browser_snapshot with offset set to ${nextOffset}]`;
|
|
180
|
+
}
|
|
144
181
|
lines.push('```yaml');
|
|
145
|
-
lines.push(
|
|
182
|
+
lines.push(truncatedAriaSnapshot.trimEnd());
|
|
146
183
|
lines.push('```');
|
|
147
184
|
return lines.join('\n');
|
|
148
185
|
}
|
package/lib/tools/snapshot.js
CHANGED
|
@@ -23,12 +23,14 @@ const snapshot = defineTool({
|
|
|
23
23
|
name: 'browser_snapshot',
|
|
24
24
|
title: 'Page snapshot',
|
|
25
25
|
description: 'Capture accessibility snapshot of the current page, this is better than screenshot',
|
|
26
|
-
inputSchema: z.object({
|
|
26
|
+
inputSchema: z.object({
|
|
27
|
+
offset: z.number().int().optional().describe('Used for pagination. Starts reading the snapshot from this line offset. Use the offset provided in the truncation message.'),
|
|
28
|
+
}),
|
|
27
29
|
type: 'readOnly',
|
|
28
30
|
},
|
|
29
31
|
handle: async (context, params, response) => {
|
|
30
32
|
await context.ensureTab();
|
|
31
|
-
response.setIncludeSnapshot();
|
|
33
|
+
response.setIncludeSnapshot(params.offset);
|
|
32
34
|
},
|
|
33
35
|
});
|
|
34
36
|
export const elementSchema = z.object({
|