brave-real-browser-mcp-server 2.27.8 → 2.27.9
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/dist/handlers/advanced-tools.js +213 -1
- package/dist/tool-definitions.js +21 -1
- package/package.json +2 -2
|
@@ -364,10 +364,221 @@ export async function handleFindElementAdvanced(page, args) {
|
|
|
364
364
|
};
|
|
365
365
|
}
|
|
366
366
|
/**
|
|
367
|
-
* Extract embedded JSON/API data from page
|
|
367
|
+
* Extract embedded JSON/API data from page + Advanced Decode capabilities
|
|
368
|
+
* Supports: base64, URL encoding, hex, rot13, reverse - with multi-layer decoding
|
|
368
369
|
*/
|
|
370
|
+
// ============================================================
|
|
371
|
+
// DECODE HELPER FUNCTIONS
|
|
372
|
+
// ============================================================
|
|
373
|
+
function decodeBase64(input) {
|
|
374
|
+
try {
|
|
375
|
+
// Handle URL-safe base64
|
|
376
|
+
const normalized = input.replace(/-/g, '+').replace(/_/g, '/');
|
|
377
|
+
const padded = normalized + '='.repeat((4 - normalized.length % 4) % 4);
|
|
378
|
+
return Buffer.from(padded, 'base64').toString('utf-8');
|
|
379
|
+
}
|
|
380
|
+
catch {
|
|
381
|
+
return input;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
function decodeUrl(input) {
|
|
385
|
+
try {
|
|
386
|
+
return decodeURIComponent(input);
|
|
387
|
+
}
|
|
388
|
+
catch {
|
|
389
|
+
return input;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
function decodeHex(input) {
|
|
393
|
+
try {
|
|
394
|
+
// Remove 0x prefix if present
|
|
395
|
+
const hex = input.replace(/^0x/i, '').replace(/\s/g, '');
|
|
396
|
+
return Buffer.from(hex, 'hex').toString('utf-8');
|
|
397
|
+
}
|
|
398
|
+
catch {
|
|
399
|
+
return input;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
function decodeRot13(input) {
|
|
403
|
+
return input.replace(/[a-zA-Z]/g, (char) => {
|
|
404
|
+
const base = char <= 'Z' ? 65 : 97;
|
|
405
|
+
return String.fromCharCode(((char.charCodeAt(0) - base + 13) % 26) + base);
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
function reverseString(input) {
|
|
409
|
+
return input.split('').reverse().join('');
|
|
410
|
+
}
|
|
411
|
+
function applyDecodeLayer(input, layer) {
|
|
412
|
+
switch (layer) {
|
|
413
|
+
case 'base64': return decodeBase64(input);
|
|
414
|
+
case 'url': return decodeUrl(input);
|
|
415
|
+
case 'hex': return decodeHex(input);
|
|
416
|
+
case 'rot13': return decodeRot13(input);
|
|
417
|
+
case 'reverse': return reverseString(input);
|
|
418
|
+
default: return input;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
function multiLayerDecode(input, layers) {
|
|
422
|
+
let result = input;
|
|
423
|
+
for (const layer of layers) {
|
|
424
|
+
result = applyDecodeLayer(result, layer);
|
|
425
|
+
}
|
|
426
|
+
return result;
|
|
427
|
+
}
|
|
428
|
+
function extractUrlParam(url, param = 'id') {
|
|
429
|
+
try {
|
|
430
|
+
const urlObj = new URL(url);
|
|
431
|
+
return urlObj.searchParams.get(param);
|
|
432
|
+
}
|
|
433
|
+
catch {
|
|
434
|
+
// Try regex fallback for malformed URLs
|
|
435
|
+
const match = url.match(new RegExp(`[?&]${param}=([^&]+)`));
|
|
436
|
+
return match ? match[1] : null;
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
function findBase64InText(text, pattern) {
|
|
440
|
+
const results = [];
|
|
441
|
+
// Use custom pattern or default base64 pattern
|
|
442
|
+
const regex = pattern
|
|
443
|
+
? new RegExp(pattern, 'g')
|
|
444
|
+
: /[A-Za-z0-9+/=_-]{20,}/g; // Match potential base64 strings
|
|
445
|
+
let match;
|
|
446
|
+
while ((match = regex.exec(text)) !== null) {
|
|
447
|
+
// Validate it looks like base64
|
|
448
|
+
const candidate = match[0];
|
|
449
|
+
if (candidate.length >= 20 && /^[A-Za-z0-9+/=_-]+$/.test(candidate)) {
|
|
450
|
+
try {
|
|
451
|
+
const decoded = decodeBase64(candidate);
|
|
452
|
+
// Only include if it decoded to something meaningful
|
|
453
|
+
if (decoded && decoded.length > 0 && /^[\x20-\x7E\s]+$/.test(decoded)) {
|
|
454
|
+
results.push(decoded);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
catch {
|
|
458
|
+
// Skip invalid base64
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
if (results.length >= 50)
|
|
462
|
+
break; // Limit results
|
|
463
|
+
}
|
|
464
|
+
return results;
|
|
465
|
+
}
|
|
466
|
+
// ============================================================
|
|
467
|
+
// MAIN HANDLER
|
|
468
|
+
// ============================================================
|
|
369
469
|
export async function handleExtractJson(page, args) {
|
|
370
470
|
const data = [];
|
|
471
|
+
let decoded = null;
|
|
472
|
+
// ============================================================
|
|
473
|
+
// 1. DECODE FROM DIRECT BASE64 STRING
|
|
474
|
+
// ============================================================
|
|
475
|
+
if (args.decodeBase64) {
|
|
476
|
+
try {
|
|
477
|
+
const result = decodeBase64(args.decodeBase64);
|
|
478
|
+
decoded = {
|
|
479
|
+
source: 'decodeBase64',
|
|
480
|
+
input: args.decodeBase64.substring(0, 50) + '...',
|
|
481
|
+
output: result,
|
|
482
|
+
isUrl: result.startsWith('http'),
|
|
483
|
+
};
|
|
484
|
+
data.push(decoded);
|
|
485
|
+
}
|
|
486
|
+
catch (e) {
|
|
487
|
+
decoded = { source: 'decodeBase64', error: String(e) };
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
// ============================================================
|
|
491
|
+
// 2. DECODE FROM URL PARAMETER
|
|
492
|
+
// ============================================================
|
|
493
|
+
if (args.decodeFromUrl) {
|
|
494
|
+
try {
|
|
495
|
+
const paramName = args.decodeUrlParam || 'id';
|
|
496
|
+
const encoded = extractUrlParam(args.decodeFromUrl, paramName);
|
|
497
|
+
if (encoded) {
|
|
498
|
+
const result = decodeBase64(encoded);
|
|
499
|
+
decoded = {
|
|
500
|
+
source: 'decodeFromUrl',
|
|
501
|
+
url: args.decodeFromUrl.substring(0, 80) + '...',
|
|
502
|
+
param: paramName,
|
|
503
|
+
encoded: encoded.substring(0, 50) + '...',
|
|
504
|
+
decoded: result,
|
|
505
|
+
isUrl: result.startsWith('http'),
|
|
506
|
+
};
|
|
507
|
+
data.push(decoded);
|
|
508
|
+
}
|
|
509
|
+
else {
|
|
510
|
+
decoded = { source: 'decodeFromUrl', error: `Parameter '${paramName}' not found in URL` };
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
catch (e) {
|
|
514
|
+
decoded = { source: 'decodeFromUrl', error: String(e) };
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
// ============================================================
|
|
518
|
+
// 3. ADVANCED MULTI-LAYER DECODE
|
|
519
|
+
// ============================================================
|
|
520
|
+
if (args.advancedDecode) {
|
|
521
|
+
try {
|
|
522
|
+
let input = args.advancedDecode.input || '';
|
|
523
|
+
// Extract from page if requested
|
|
524
|
+
if (args.advancedDecode.extractFromPage) {
|
|
525
|
+
const pageContent = await page.evaluate((selector) => {
|
|
526
|
+
if (selector) {
|
|
527
|
+
const el = document.querySelector(selector);
|
|
528
|
+
return el?.textContent || '';
|
|
529
|
+
}
|
|
530
|
+
return document.body?.textContent || '';
|
|
531
|
+
}, args.advancedDecode.pageSelector);
|
|
532
|
+
input = pageContent;
|
|
533
|
+
}
|
|
534
|
+
if (input) {
|
|
535
|
+
const result = multiLayerDecode(input, args.advancedDecode.layers);
|
|
536
|
+
decoded = {
|
|
537
|
+
source: 'advancedDecode',
|
|
538
|
+
layers: args.advancedDecode.layers,
|
|
539
|
+
inputLength: input.length,
|
|
540
|
+
inputPreview: input.substring(0, 100) + (input.length > 100 ? '...' : ''),
|
|
541
|
+
output: result,
|
|
542
|
+
outputLength: result.length,
|
|
543
|
+
isUrl: result.startsWith('http'),
|
|
544
|
+
};
|
|
545
|
+
data.push(decoded);
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
catch (e) {
|
|
549
|
+
decoded = { source: 'advancedDecode', error: String(e) };
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
// ============================================================
|
|
553
|
+
// 4. FIND AND DECODE BASE64 PATTERNS IN PAGE
|
|
554
|
+
// ============================================================
|
|
555
|
+
if (args.decodePattern) {
|
|
556
|
+
try {
|
|
557
|
+
const pageText = await page.evaluate(() => {
|
|
558
|
+
// Get all script contents + page text
|
|
559
|
+
const scripts = Array.from(document.querySelectorAll('script'))
|
|
560
|
+
.map(s => s.textContent || '')
|
|
561
|
+
.join('\n');
|
|
562
|
+
const bodyText = document.body?.textContent || '';
|
|
563
|
+
return scripts + '\n' + bodyText;
|
|
564
|
+
});
|
|
565
|
+
const found = findBase64InText(pageText, args.decodePattern);
|
|
566
|
+
decoded = {
|
|
567
|
+
source: 'decodePattern',
|
|
568
|
+
pattern: args.decodePattern,
|
|
569
|
+
foundCount: found.length,
|
|
570
|
+
results: found.slice(0, 20), // Limit to first 20
|
|
571
|
+
urls: found.filter(f => f.startsWith('http')),
|
|
572
|
+
};
|
|
573
|
+
data.push(decoded);
|
|
574
|
+
}
|
|
575
|
+
catch (e) {
|
|
576
|
+
decoded = { source: 'decodePattern', error: String(e) };
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
// ============================================================
|
|
580
|
+
// 5. ORIGINAL JSON EXTRACTION (preserved)
|
|
581
|
+
// ============================================================
|
|
371
582
|
// Extract from script tags with type="application/json" or type="application/ld+json"
|
|
372
583
|
const scriptData = await page.evaluate((selector) => {
|
|
373
584
|
const scripts = selector
|
|
@@ -415,6 +626,7 @@ export async function handleExtractJson(page, args) {
|
|
|
415
626
|
found: data.length > 0,
|
|
416
627
|
data,
|
|
417
628
|
count: data.length,
|
|
629
|
+
decoded,
|
|
418
630
|
};
|
|
419
631
|
}
|
|
420
632
|
/**
|
package/dist/tool-definitions.js
CHANGED
|
@@ -470,14 +470,34 @@ export const TOOLS = [
|
|
|
470
470
|
},
|
|
471
471
|
{
|
|
472
472
|
name: 'extract_json',
|
|
473
|
-
description: 'Extract embedded JSON/API data from page (LD+JSON, __NEXT_DATA__, etc.)',
|
|
473
|
+
description: 'Extract embedded JSON/API data from page (LD+JSON, __NEXT_DATA__, etc.) + Advanced Decode capabilities for complex websites (base64, URL, hex, rot13, multi-layer)',
|
|
474
474
|
inputSchema: {
|
|
475
475
|
type: 'object',
|
|
476
476
|
additionalProperties: false,
|
|
477
477
|
properties: {
|
|
478
|
+
// Original JSON extraction
|
|
478
479
|
selector: { type: 'string', description: 'CSS selector for script tags' },
|
|
479
480
|
scriptIndex: { type: 'number', description: 'Index of script to extract' },
|
|
480
481
|
variableName: { type: 'string', description: 'Window variable name to extract' },
|
|
482
|
+
// Advanced Decode capabilities
|
|
483
|
+
decodeBase64: { type: 'string', description: 'Direct base64 string to decode' },
|
|
484
|
+
decodeFromUrl: { type: 'string', description: 'URL to extract and decode encoded parameter from (e.g., gadgetsweb.xyz/?id=xxx)' },
|
|
485
|
+
decodeUrlParam: { type: 'string', description: 'URL parameter name to decode (default: id)', default: 'id' },
|
|
486
|
+
decodePattern: { type: 'string', description: 'Regex pattern to find and decode base64 strings in page content' },
|
|
487
|
+
advancedDecode: {
|
|
488
|
+
type: 'object',
|
|
489
|
+
description: 'Multi-layer decode for complex obfuscation (supports base64, url, hex, rot13, reverse)',
|
|
490
|
+
properties: {
|
|
491
|
+
input: { type: 'string', description: 'Input string to decode (if not using page content)' },
|
|
492
|
+
layers: {
|
|
493
|
+
type: 'array',
|
|
494
|
+
items: { type: 'string', enum: ['base64', 'url', 'hex', 'rot13', 'reverse'] },
|
|
495
|
+
description: 'Decode layers to apply in order (e.g., ["base64", "url"])'
|
|
496
|
+
},
|
|
497
|
+
extractFromPage: { type: 'boolean', description: 'Extract input from current page content', default: false },
|
|
498
|
+
pageSelector: { type: 'string', description: 'CSS selector to extract from (for extractFromPage)' },
|
|
499
|
+
},
|
|
500
|
+
},
|
|
481
501
|
},
|
|
482
502
|
},
|
|
483
503
|
},
|
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.9",
|
|
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.9",
|
|
54
54
|
"puppeteer-core": "^24.35.0",
|
|
55
55
|
"turndown": "latest",
|
|
56
56
|
"vscode-languageserver": "^9.0.1",
|