brave-real-browser-mcp-server 2.27.26 → 2.27.28
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.
|
@@ -195,21 +195,137 @@ export async function handleMultiLayerRedirectTrace(page, args) {
|
|
|
195
195
|
};
|
|
196
196
|
}
|
|
197
197
|
/**
|
|
198
|
-
* 🔥 ULTRA-POWERFUL Regex Engine (like regex101.com)
|
|
198
|
+
* 🔥 ULTRA-POWERFUL Regex Engine (like regex101.com) + Simple Search Mode
|
|
199
199
|
* Features: Named capture groups, all regex flags, match highlighting,
|
|
200
|
-
* detailed match info, replace mode, timeout protection
|
|
200
|
+
* detailed match info, replace mode, timeout protection,
|
|
201
|
+
* NEW: Simple search mode, Pattern explanation, Hierarchical group tree
|
|
201
202
|
*/
|
|
203
|
+
// Helper: Escape regex special characters for simple search mode
|
|
204
|
+
function escapeRegex(str) {
|
|
205
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
206
|
+
}
|
|
207
|
+
// Helper: Generate pattern explanation
|
|
208
|
+
function explainPattern(pattern) {
|
|
209
|
+
const explanations = [];
|
|
210
|
+
const regexTokens = [
|
|
211
|
+
{ regex: /^\^/, meaning: () => 'Start of string/line' },
|
|
212
|
+
{ regex: /^\$/, meaning: () => 'End of string/line' },
|
|
213
|
+
{ regex: /^\\d/, meaning: () => 'Any digit (0-9)' },
|
|
214
|
+
{ regex: /^\\D/, meaning: () => 'Any non-digit character' },
|
|
215
|
+
{ regex: /^\\w/, meaning: () => 'Word character (a-z, A-Z, 0-9, _)' },
|
|
216
|
+
{ regex: /^\\W/, meaning: () => 'Non-word character' },
|
|
217
|
+
{ regex: /^\\s/, meaning: () => 'Whitespace character (space, tab, newline)' },
|
|
218
|
+
{ regex: /^\\S/, meaning: () => 'Non-whitespace character' },
|
|
219
|
+
{ regex: /^\\b/, meaning: () => 'Word boundary' },
|
|
220
|
+
{ regex: /^\\B/, meaning: () => 'Non-word boundary' },
|
|
221
|
+
{ regex: /^\\n/, meaning: () => 'Newline character' },
|
|
222
|
+
{ regex: /^\\r/, meaning: () => 'Carriage return' },
|
|
223
|
+
{ regex: /^\\t/, meaning: () => 'Tab character' },
|
|
224
|
+
{ regex: /^\./, meaning: () => 'Any character (except newline unless s flag)' },
|
|
225
|
+
{ regex: /^\*/, meaning: () => 'Zero or more of the preceding' },
|
|
226
|
+
{ regex: /^\+/, meaning: () => 'One or more of the preceding' },
|
|
227
|
+
{ regex: /^\?/, meaning: () => 'Zero or one of the preceding (optional)' },
|
|
228
|
+
{ regex: /^\*\?/, meaning: () => 'Zero or more (lazy/non-greedy)' },
|
|
229
|
+
{ regex: /^\+\?/, meaning: () => 'One or more (lazy/non-greedy)' },
|
|
230
|
+
{ regex: /^\?\?/, meaning: () => 'Zero or one (lazy/non-greedy)' },
|
|
231
|
+
{ regex: /^\|/, meaning: () => 'OR - matches either side' },
|
|
232
|
+
{ regex: /^\(\?:([^)]*)\)/, meaning: (m) => `Non-capturing group: ${m}` },
|
|
233
|
+
{ regex: /^\(\?=([^)]*)\)/, meaning: (m) => `Positive lookahead: must be followed by "${m}"` },
|
|
234
|
+
{ regex: /^\(\?!([^)]*)\)/, meaning: (m) => `Negative lookahead: must NOT be followed by "${m}"` },
|
|
235
|
+
{ regex: /^\(\?<=([^)]*)\)/, meaning: (m) => `Positive lookbehind: must be preceded by "${m}"` },
|
|
236
|
+
{ regex: /^\(\?<!([^)]*)\)/, meaning: (m) => `Negative lookbehind: must NOT be preceded by "${m}"` },
|
|
237
|
+
{ regex: /^\(\?<(\w+)>/, meaning: (m) => `Named capture group: "${m}"` },
|
|
238
|
+
{ regex: /^\(/, meaning: () => 'Capture group start' },
|
|
239
|
+
{ regex: /^\)/, meaning: () => 'Capture group end' },
|
|
240
|
+
{ regex: /^\[(\^)?([^\]]+)\]/, meaning: (m) => m.startsWith('^') ? `Any character NOT in: ${m.slice(1)}` : `Any character in: ${m}` },
|
|
241
|
+
{ regex: /^\{(\d+)\}/, meaning: (m) => `Exactly ${m} occurrences` },
|
|
242
|
+
{ regex: /^\{(\d+),\}/, meaning: (m) => `${m} or more occurrences` },
|
|
243
|
+
{ regex: /^\{(\d+),(\d+)\}/, meaning: (m) => `Between ${m.split(',')[0]} and ${m.split(',')[1]} occurrences` },
|
|
244
|
+
{ regex: /^\\([0-9])/, meaning: (m) => `Backreference to group ${m}` },
|
|
245
|
+
{ regex: /^\\k<(\w+)>/, meaning: (m) => `Backreference to named group "${m}"` },
|
|
246
|
+
{ regex: /^https?:\/\//, meaning: () => 'HTTP or HTTPS protocol' },
|
|
247
|
+
{ regex: /^[a-zA-Z0-9]+/, meaning: (m) => `Literal text: "${m}"` },
|
|
248
|
+
{ regex: /^\\(.)/, meaning: (m) => `Escaped character: "${m}"` },
|
|
249
|
+
];
|
|
250
|
+
let remaining = pattern;
|
|
251
|
+
let position = 0;
|
|
252
|
+
while (remaining.length > 0) {
|
|
253
|
+
let matched = false;
|
|
254
|
+
for (const token of regexTokens) {
|
|
255
|
+
const match = remaining.match(token.regex);
|
|
256
|
+
if (match) {
|
|
257
|
+
const part = match[0];
|
|
258
|
+
const captured = match[1] || match[0];
|
|
259
|
+
explanations.push({
|
|
260
|
+
part,
|
|
261
|
+
meaning: token.meaning(captured),
|
|
262
|
+
position,
|
|
263
|
+
});
|
|
264
|
+
remaining = remaining.slice(part.length);
|
|
265
|
+
position += part.length;
|
|
266
|
+
matched = true;
|
|
267
|
+
break;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
if (!matched) {
|
|
271
|
+
// Single character literal
|
|
272
|
+
const char = remaining[0];
|
|
273
|
+
explanations.push({
|
|
274
|
+
part: char,
|
|
275
|
+
meaning: `Literal character: "${char}"`,
|
|
276
|
+
position,
|
|
277
|
+
});
|
|
278
|
+
remaining = remaining.slice(1);
|
|
279
|
+
position += 1;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
return explanations;
|
|
283
|
+
}
|
|
284
|
+
// Helper: Build hierarchical group tree
|
|
285
|
+
function buildGroupTree(pattern, match) {
|
|
286
|
+
const groups = [];
|
|
287
|
+
// Extract named groups from pattern
|
|
288
|
+
const namedGroupPattern = /\(\?<(\w+)>/g;
|
|
289
|
+
const namedGroups = [];
|
|
290
|
+
let namedMatch;
|
|
291
|
+
while ((namedMatch = namedGroupPattern.exec(pattern)) !== null) {
|
|
292
|
+
namedGroups.push(namedMatch[1]);
|
|
293
|
+
}
|
|
294
|
+
// Build group entries
|
|
295
|
+
for (let i = 1; i < match.length; i++) {
|
|
296
|
+
if (match[i] !== undefined) {
|
|
297
|
+
const value = match[i];
|
|
298
|
+
const fullMatchText = match[0];
|
|
299
|
+
const valueIndex = fullMatchText.indexOf(value);
|
|
300
|
+
groups.push({
|
|
301
|
+
index: i,
|
|
302
|
+
name: match.groups && Object.keys(match.groups)[i - 1] ? Object.keys(match.groups).find(k => match.groups[k] === value) : undefined,
|
|
303
|
+
value,
|
|
304
|
+
start: valueIndex >= 0 ? match.index + valueIndex : match.index,
|
|
305
|
+
end: valueIndex >= 0 ? match.index + valueIndex + value.length : match.index + value.length,
|
|
306
|
+
nested: i > 1 && match[i - 1]?.includes(value),
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
return {
|
|
311
|
+
fullMatch: match[0],
|
|
312
|
+
groups,
|
|
313
|
+
};
|
|
314
|
+
}
|
|
202
315
|
export async function handleSearchRegex(page, args) {
|
|
203
316
|
const progressNotifier = getProgressNotifier();
|
|
204
317
|
const tracker = progressNotifier.createTracker(`search-regex-${Date.now()}`);
|
|
205
|
-
|
|
318
|
+
// Determine search mode
|
|
319
|
+
const isSimpleSearch = args.simpleSearch === true;
|
|
320
|
+
const searchMode = isSimpleSearch ? '🔤 Simple Search' : '🔎 Regex Search';
|
|
321
|
+
tracker.start(100, `${searchMode}: "${args.pattern}"`);
|
|
206
322
|
// Build regex flags
|
|
207
323
|
const flags = args.flags || {};
|
|
208
324
|
let flagString = '';
|
|
209
325
|
if (flags.global !== false)
|
|
210
326
|
flagString += 'g';
|
|
211
|
-
if (flags.ignoreCase)
|
|
212
|
-
flagString += 'i';
|
|
327
|
+
if (flags.ignoreCase || isSimpleSearch)
|
|
328
|
+
flagString += 'i'; // Simple search defaults to case-insensitive
|
|
213
329
|
if (flags.multiline)
|
|
214
330
|
flagString += 'm';
|
|
215
331
|
if (flags.dotAll)
|
|
@@ -218,11 +334,24 @@ export async function handleSearchRegex(page, args) {
|
|
|
218
334
|
flagString += 'u';
|
|
219
335
|
if (flags.sticky)
|
|
220
336
|
flagString += 'y';
|
|
337
|
+
// Process pattern based on mode
|
|
338
|
+
let processedPattern = args.pattern;
|
|
339
|
+
const originalPattern = args.pattern;
|
|
340
|
+
if (isSimpleSearch) {
|
|
341
|
+
// Escape all regex special characters for literal search
|
|
342
|
+
processedPattern = escapeRegex(args.pattern);
|
|
343
|
+
tracker.setProgress(10, '🔤 Simple search mode - treating as literal text');
|
|
344
|
+
}
|
|
221
345
|
// Validate regex
|
|
222
346
|
let regex;
|
|
223
|
-
let patternInfo = {
|
|
347
|
+
let patternInfo = {
|
|
348
|
+
flags: flagString,
|
|
349
|
+
isValid: true,
|
|
350
|
+
isSimpleSearch,
|
|
351
|
+
originalPattern: isSimpleSearch ? originalPattern : undefined,
|
|
352
|
+
};
|
|
224
353
|
try {
|
|
225
|
-
regex = new RegExp(
|
|
354
|
+
regex = new RegExp(processedPattern, flagString);
|
|
226
355
|
}
|
|
227
356
|
catch (e) {
|
|
228
357
|
tracker.fail(`Invalid regex: ${e.message}`);
|
|
@@ -230,9 +359,15 @@ export async function handleSearchRegex(page, args) {
|
|
|
230
359
|
found: false,
|
|
231
360
|
matches: [],
|
|
232
361
|
count: 0,
|
|
233
|
-
patternInfo: {
|
|
362
|
+
patternInfo: { ...patternInfo, isValid: false, errorMessage: e.message },
|
|
234
363
|
};
|
|
235
364
|
}
|
|
365
|
+
// Generate pattern explanation if requested
|
|
366
|
+
let patternExplanation;
|
|
367
|
+
if (args.showExplanation && !isSimpleSearch) {
|
|
368
|
+
tracker.setProgress(15, '📖 Generating pattern explanation...');
|
|
369
|
+
patternExplanation = explainPattern(args.pattern);
|
|
370
|
+
}
|
|
236
371
|
tracker.setProgress(20, '📄 Extracting content...');
|
|
237
372
|
// Get content based on sourceType
|
|
238
373
|
let content = '';
|
|
@@ -288,9 +423,9 @@ export async function handleSearchRegex(page, args) {
|
|
|
288
423
|
}
|
|
289
424
|
if (!content) {
|
|
290
425
|
tracker.fail('No content to search');
|
|
291
|
-
return { found: false, matches: [], count: 0, patternInfo };
|
|
426
|
+
return { found: false, matches: [], count: 0, patternInfo, patternExplanation };
|
|
292
427
|
}
|
|
293
|
-
tracker.setProgress(50, '🔍 Running
|
|
428
|
+
tracker.setProgress(50, '🔍 Running match...');
|
|
294
429
|
const matches = [];
|
|
295
430
|
const maxMatches = args.maxMatches || 100;
|
|
296
431
|
const contextChars = args.contextChars || 50;
|
|
@@ -319,7 +454,7 @@ export async function handleSearchRegex(page, args) {
|
|
|
319
454
|
length: match[0].length,
|
|
320
455
|
};
|
|
321
456
|
// Extract groups if requested
|
|
322
|
-
if (args.extractGroups !== false) {
|
|
457
|
+
if (args.extractGroups !== false && !isSimpleSearch) {
|
|
323
458
|
if (match.groups) {
|
|
324
459
|
matchResult.groups = match.groups;
|
|
325
460
|
}
|
|
@@ -327,6 +462,10 @@ export async function handleSearchRegex(page, args) {
|
|
|
327
462
|
matchResult.numberedGroups = match.slice(1);
|
|
328
463
|
}
|
|
329
464
|
}
|
|
465
|
+
// Build hierarchical group tree if requested
|
|
466
|
+
if (args.showGroupTree && !isSimpleSearch && match.length > 1) {
|
|
467
|
+
matchResult.groupTree = buildGroupTree(args.pattern, match);
|
|
468
|
+
}
|
|
330
469
|
matches.push(matchResult);
|
|
331
470
|
if (matches.length >= maxMatches)
|
|
332
471
|
break;
|
|
@@ -341,13 +480,15 @@ export async function handleSearchRegex(page, args) {
|
|
|
341
480
|
tracker.setProgress(90, '🔄 Applying replacement...');
|
|
342
481
|
replaced = content.replace(regex, args.replaceWith);
|
|
343
482
|
}
|
|
344
|
-
|
|
483
|
+
const modeText = isSimpleSearch ? 'text matches' : 'regex matches';
|
|
484
|
+
tracker.complete(`🎉 Found ${matches.length} ${modeText}`);
|
|
345
485
|
return {
|
|
346
486
|
found: matches.length > 0,
|
|
347
487
|
matches,
|
|
348
488
|
count: matches.length,
|
|
349
489
|
replaced,
|
|
350
490
|
patternInfo,
|
|
491
|
+
patternExplanation,
|
|
351
492
|
};
|
|
352
493
|
}
|
|
353
494
|
/**
|
|
@@ -171,15 +171,18 @@ const TOOL_DEFINITIONS = {
|
|
|
171
171
|
// Advanced Tools
|
|
172
172
|
search_regex: {
|
|
173
173
|
name: 'search_regex',
|
|
174
|
-
description: '🔥 ULTRA-POWERFUL Regex Engine (like regex101.com) - Full regex support with flags, capture groups, replace mode, timeout protection',
|
|
174
|
+
description: '🔥 ULTRA-POWERFUL Regex Engine (like regex101.com) - Full regex support with flags, capture groups, replace mode, timeout protection. NEW: Simple search mode for non-regex text search, pattern explanation, and group tree.',
|
|
175
175
|
category: 'Advanced',
|
|
176
176
|
parameters: [
|
|
177
|
-
{ name: 'pattern', type: 'string', description: 'Regex pattern to search', required: true },
|
|
177
|
+
{ name: 'pattern', type: 'string', description: 'Regex pattern to search (or literal text in simpleSearch mode)', required: true },
|
|
178
|
+
{ name: 'simpleSearch', type: 'boolean', description: 'Treat pattern as literal text, not regex. Perfect for Ctrl+F style searches.', required: false, default: false },
|
|
178
179
|
{ name: 'flags', type: 'object', description: 'Regex flags: global, ignoreCase, multiline, dotAll, unicode, sticky', required: false },
|
|
179
180
|
{ name: 'replaceWith', type: 'string', description: 'Replace matches with this string (supports $1, $2, $&)', required: false },
|
|
180
181
|
{ name: 'sourceType', type: 'string', description: 'Where to search: text, html, scripts, styles, attributes, all', required: false, default: 'text' },
|
|
181
182
|
{ name: 'extractGroups', type: 'boolean', description: 'Extract capture groups', required: false, default: true },
|
|
182
183
|
{ name: 'highlightMatches', type: 'boolean', description: 'Highlight matches in context', required: false, default: false },
|
|
184
|
+
{ name: 'showExplanation', type: 'boolean', description: 'Generate explanation for each regex component', required: false, default: false },
|
|
185
|
+
{ name: 'showGroupTree', type: 'boolean', description: 'Show hierarchical structure of capture groups', required: false, default: false },
|
|
183
186
|
],
|
|
184
187
|
},
|
|
185
188
|
// web_search REMOVED - redundant with search_regex
|
package/dist/tool-definitions.js
CHANGED
|
@@ -476,14 +476,16 @@ export const TOOLS = [
|
|
|
476
476
|
},
|
|
477
477
|
{
|
|
478
478
|
name: 'search_regex',
|
|
479
|
-
description: `🔥 ULTRA-POWERFUL Regex Engine (like regex101.com) - Test, match, replace with full regex support. Features: Named capture groups, all regex flags (g/i/m/s/u/y), match highlighting, detailed match info with indices, replace mode, pattern explanation, timeout protection against catastrophic backtracking. Search in HTML, text, scripts, styles, or attributes.`,
|
|
479
|
+
description: `🔥 ULTRA-POWERFUL Regex Engine (like regex101.com) - Test, match, replace with full regex support. Features: Named capture groups, all regex flags (g/i/m/s/u/y), match highlighting, detailed match info with indices, replace mode, pattern explanation, timeout protection against catastrophic backtracking. Search in HTML, text, scripts, styles, or attributes. NEW: Simple search mode for non-regex text search.`,
|
|
480
480
|
inputSchema: {
|
|
481
481
|
type: 'object',
|
|
482
482
|
additionalProperties: false,
|
|
483
483
|
properties: {
|
|
484
484
|
// Core regex
|
|
485
|
-
pattern: { type: 'string', description: 'Regex pattern to search (supports full JavaScript regex syntax including lookahead/lookbehind)' },
|
|
485
|
+
pattern: { type: 'string', description: 'Regex pattern to search (supports full JavaScript regex syntax including lookahead/lookbehind). In simpleSearch mode, treated as literal text.' },
|
|
486
486
|
testString: { type: 'string', description: 'Optional custom string to test against. If not provided, uses page content' },
|
|
487
|
+
// NEW: Simple Search Mode
|
|
488
|
+
simpleSearch: { type: 'boolean', description: 'Treat pattern as literal text, not regex. Perfect for Ctrl+F style searches. Automatically case-insensitive.', default: false },
|
|
487
489
|
// Regex flags (individually controllable like regex101.com)
|
|
488
490
|
flags: {
|
|
489
491
|
type: 'object',
|
|
@@ -515,6 +517,10 @@ export const TOOLS = [
|
|
|
515
517
|
extractGroups: { type: 'boolean', description: 'Extract and return named/numbered capture groups', default: true },
|
|
516
518
|
highlightMatches: { type: 'boolean', description: 'Return matches with <<MATCH>> highlighting', default: false },
|
|
517
519
|
showMatchInfo: { type: 'boolean', description: 'Show detailed match info: index, length, groups', default: true },
|
|
520
|
+
// NEW: Pattern Explanation
|
|
521
|
+
showExplanation: { type: 'boolean', description: 'Generate explanation for each regex component (like regex101.com explanation sidebar)', default: false },
|
|
522
|
+
// NEW: Hierarchical Group Tree
|
|
523
|
+
showGroupTree: { type: 'boolean', description: 'Show hierarchical structure of capture groups with positions and nesting info', default: false },
|
|
518
524
|
// Safety
|
|
519
525
|
timeout: { type: 'number', description: 'Regex execution timeout in ms (prevents catastrophic backtracking)', default: 5000 },
|
|
520
526
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "brave-real-browser-mcp-server",
|
|
3
|
-
"version": "2.27.
|
|
3
|
+
"version": "2.27.28",
|
|
4
4
|
"description": "🦁 MCP server for Brave Real Browser - NPM Workspaces Monorepo with anti-detection features, SSE streaming, and LSP compatibility",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"dependencies": {
|
|
51
51
|
"@modelcontextprotocol/sdk": "latest",
|
|
52
52
|
"@types/turndown": "latest",
|
|
53
|
-
"brave-real-browser": "^2.8.
|
|
53
|
+
"brave-real-browser": "^2.8.28",
|
|
54
54
|
"puppeteer-core": "^24.35.0",
|
|
55
55
|
"turndown": "latest",
|
|
56
56
|
"vscode-languageserver": "^9.0.1",
|