@ejazullah/browser-mcp 0.0.65 → 0.0.66

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 CHANGED
@@ -134,6 +134,7 @@ 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,
137
138
  mongodb: {
138
139
  url: cliOptions.mongodbUrl,
139
140
  dbName: cliOptions.mongodbDb,
@@ -166,6 +167,7 @@ function configFromEnv() {
166
167
  options.isolated = envToBoolean(process.env.PLAYWRIGHT_MCP_ISOLATED);
167
168
  if (process.env.PLAYWRIGHT_MCP_IMAGE_RESPONSES === 'omit')
168
169
  options.imageResponses = 'omit';
170
+ options.maxResponseChars = envToString(process.env.PLAYWRIGHT_MCP_MAX_RESPONSE_CHARS);
169
171
  options.sandbox = envToBoolean(process.env.PLAYWRIGHT_MCP_SANDBOX);
170
172
  options.outputDir = envToString(process.env.PLAYWRIGHT_MCP_OUTPUT_DIR);
171
173
  options.port = envToNumber(process.env.PLAYWRIGHT_MCP_PORT);
package/lib/program.js CHANGED
@@ -65,6 +65,7 @@ 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 100000.')
68
69
  .option('--no-sandbox', 'disable the sandbox for all process types that are normally sandboxed.')
69
70
  .option('--output-dir <path>', 'path to the directory for output files.')
70
71
  .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,11 @@ export class Response {
56
57
  images() {
57
58
  return this._images;
58
59
  }
59
- setIncludeSnapshot() {
60
+ setIncludeSnapshot(offsetLine) {
60
61
  this._includeSnapshot = true;
62
+ if (offsetLine !== undefined) {
63
+ this._snapshotOffsetLine = offsetLine;
64
+ }
61
65
  }
62
66
  setIncludeTabs() {
63
67
  this._includeTabs = true;
@@ -104,12 +108,20 @@ ${this._code.join('\n')}
104
108
  response.push('');
105
109
  }
106
110
  else if (this._tabSnapshot) {
107
- response.push(renderTabSnapshot(this._tabSnapshot));
111
+ const currentLength = response.join('\n').length;
112
+ const maxChars = this._context.config.maxResponseChars ?? 100000;
113
+ const remainingBudget = Math.max(1000, maxChars - currentLength);
114
+ response.push(renderTabSnapshot(this._tabSnapshot, remainingBudget, this._snapshotOffsetLine));
108
115
  response.push('');
109
116
  }
110
117
  // Main response part
118
+ let responseText = response.join('\n');
119
+ const maxChars = this._context.config.maxResponseChars ?? 100000;
120
+ if (responseText.length > maxChars) {
121
+ responseText = responseText.slice(0, maxChars - 100) + '\n\n... [Response truncated due to length limits]';
122
+ }
111
123
  const content = [
112
- { type: 'text', text: response.join('\n') },
124
+ { type: 'text', text: responseText },
113
125
  ];
114
126
  // Image attachments.
115
127
  if (this._context.config.imageResponses !== 'omit') {
@@ -119,7 +131,7 @@ ${this._code.join('\n')}
119
131
  return { content, isError: this._isError };
120
132
  }
121
133
  }
122
- function renderTabSnapshot(tabSnapshot) {
134
+ function renderTabSnapshot(tabSnapshot, maxLength = Infinity, offsetLine = 0) {
123
135
  const lines = [];
124
136
  if (tabSnapshot.consoleMessages.length) {
125
137
  lines.push(`### New console messages`);
@@ -141,8 +153,30 @@ function renderTabSnapshot(tabSnapshot) {
141
153
  lines.push(`- Page URL: ${tabSnapshot.url}`);
142
154
  lines.push(`- Page Title: ${tabSnapshot.title}`);
143
155
  lines.push(`- Page Snapshot:`);
156
+ const prefix = '```yaml\n';
157
+ const suffix = '\n```';
158
+ const currentLength = lines.join('\n').length + prefix.length + suffix.length;
159
+ const budget = maxLength - currentLength;
160
+ let ariaSnapshotLines = tabSnapshot.ariaSnapshot.split('\n');
161
+ if (offsetLine > 0) {
162
+ ariaSnapshotLines = ariaSnapshotLines.slice(offsetLine);
163
+ }
164
+ let includedLines = 0;
165
+ let currentStringLength = 0;
166
+ let truncatedAriaSnapshot = '';
167
+ for (const line of ariaSnapshotLines) {
168
+ if (currentStringLength + line.length + 1 > budget)
169
+ break;
170
+ truncatedAriaSnapshot += line + '\n';
171
+ currentStringLength += line.length + 1;
172
+ includedLines++;
173
+ }
174
+ if (includedLines < ariaSnapshotLines.length) {
175
+ const nextOffset = offsetLine + includedLines;
176
+ truncatedAriaSnapshot += `\n... [ARIA Snapshot truncated due to length limits. To see more, call browser_snapshot with offset set to ${nextOffset}]`;
177
+ }
144
178
  lines.push('```yaml');
145
- lines.push(tabSnapshot.ariaSnapshot);
179
+ lines.push(truncatedAriaSnapshot.trimEnd());
146
180
  lines.push('```');
147
181
  return lines.join('\n');
148
182
  }
@@ -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({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ejazullah/browser-mcp",
3
- "version": "0.0.65",
3
+ "version": "0.0.66",
4
4
  "description": "@ejazullah/browser-mcp - Enhanced Playwright Tools for MCP with CDP Support",
5
5
  "type": "module",
6
6
  "repository": {