brave-real-browser-mcp-server 2.27.13 → 2.27.14
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 +6 -1
- package/dist/handlers/advanced-tools.js +132 -1
- package/dist/tool-definitions.js +7 -5
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -31,7 +31,12 @@
|
|
|
31
31
|
"mcpServers": {
|
|
32
32
|
"brave-real-browser": {
|
|
33
33
|
"command": "node",
|
|
34
|
-
"args": [
|
|
34
|
+
"args": [
|
|
35
|
+
"c:\\Users\\Admin\\Desktop\\Workspace-For-Brave-Real-browser-Mcp-Server\\Brave-Real-Browser-Mcp-Server\\dist\\index.js"
|
|
36
|
+
],
|
|
37
|
+
"env": {
|
|
38
|
+
"headless": "false"
|
|
39
|
+
}
|
|
35
40
|
}
|
|
36
41
|
}
|
|
37
42
|
}
|
|
@@ -408,6 +408,95 @@ function decodeRot13(input) {
|
|
|
408
408
|
function reverseString(input) {
|
|
409
409
|
return input.split('').reverse().join('');
|
|
410
410
|
}
|
|
411
|
+
/**
|
|
412
|
+
* Unpack obfuscated JavaScript code using eval(function(p,a,c,k,e,d)) pattern
|
|
413
|
+
* This is the most common packer used by streaming sites
|
|
414
|
+
*/
|
|
415
|
+
function unpackJs(input) {
|
|
416
|
+
try {
|
|
417
|
+
// Pattern: eval(function(p,a,c,k,e,d){...}('packed_code',radix,count,'dictionary'.split('|'),0,{}))
|
|
418
|
+
const packedRegex = /eval\s*\(\s*function\s*\(\s*p\s*,\s*a\s*,\s*c\s*,\s*k\s*,\s*e\s*,\s*d\s*\)\s*\{[^}]+\}\s*\(\s*'([^']+)'\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*'([^']+)'\.split\s*\(\s*'\|'\s*\)/;
|
|
419
|
+
const match = input.match(packedRegex);
|
|
420
|
+
if (!match) {
|
|
421
|
+
// Try alternate pattern without word boundary
|
|
422
|
+
const altRegex = /function\s*\(\s*p\s*,\s*a\s*,\s*c\s*,\s*k\s*,\s*e\s*,\s*d\s*\)[^}]+\}\s*\(\s*'([^']+)'\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*'([^']+)'\.split/;
|
|
423
|
+
const altMatch = input.match(altRegex);
|
|
424
|
+
if (!altMatch)
|
|
425
|
+
return input;
|
|
426
|
+
return unpackPACKer(altMatch[1], parseInt(altMatch[2]), parseInt(altMatch[3]), altMatch[4].split('|'));
|
|
427
|
+
}
|
|
428
|
+
const packed = match[1];
|
|
429
|
+
const radix = parseInt(match[2]);
|
|
430
|
+
const count = parseInt(match[3]);
|
|
431
|
+
const dictionary = match[4].split('|');
|
|
432
|
+
return unpackPACKer(packed, radix, count, dictionary);
|
|
433
|
+
}
|
|
434
|
+
catch (e) {
|
|
435
|
+
return input;
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
/**
|
|
439
|
+
* Core P.A.C.K.E.R. decoding algorithm
|
|
440
|
+
* Replaces placeholders in packed code with dictionary values
|
|
441
|
+
*/
|
|
442
|
+
function unpackPACKer(p, a, c, k) {
|
|
443
|
+
// Decode function to convert number to base-a representation
|
|
444
|
+
function decode(d) {
|
|
445
|
+
const chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
|
446
|
+
let result = '';
|
|
447
|
+
while (d > 0) {
|
|
448
|
+
result = chars[d % a] + result;
|
|
449
|
+
d = Math.floor(d / a);
|
|
450
|
+
}
|
|
451
|
+
return result || '0';
|
|
452
|
+
}
|
|
453
|
+
// Replace encoded values with dictionary words
|
|
454
|
+
while (c--) {
|
|
455
|
+
if (k[c]) {
|
|
456
|
+
// Match word boundaries using regex based on radix
|
|
457
|
+
const pattern = new RegExp('\\b' + decode(c) + '\\b', 'g');
|
|
458
|
+
p = p.replace(pattern, k[c]);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
return p;
|
|
462
|
+
}
|
|
463
|
+
/**
|
|
464
|
+
* Extract all packed scripts from HTML and decode them
|
|
465
|
+
*/
|
|
466
|
+
function extractAndUnpackAll(html) {
|
|
467
|
+
const scripts = [];
|
|
468
|
+
const urls = [];
|
|
469
|
+
// Find all packed script blocks
|
|
470
|
+
const packedPattern = /eval\s*\(\s*function\s*\(\s*p\s*,\s*a\s*,\s*c\s*,\s*k\s*,\s*e\s*,\s*d\s*\)[^)]+\([^)]+\)\s*\)/g;
|
|
471
|
+
let match;
|
|
472
|
+
while ((match = packedPattern.exec(html)) !== null) {
|
|
473
|
+
try {
|
|
474
|
+
const unpacked = unpackJs(match[0]);
|
|
475
|
+
if (unpacked && unpacked !== match[0]) {
|
|
476
|
+
scripts.push(unpacked);
|
|
477
|
+
// Extract URLs from unpacked script
|
|
478
|
+
const urlPatterns = [
|
|
479
|
+
/https?:\/\/[^\s"'<>\\]+\.m3u8[^\s"'<>\\]*/gi,
|
|
480
|
+
/https?:\/\/[^\s"'<>\\]+\.txt\?[^\s"'<>\\]*/gi,
|
|
481
|
+
/https?:\/\/[^\s"'<>\\]+\/stream\/[^\s"'<>\\]*/gi,
|
|
482
|
+
/https?:\/\/[^\s"'<>\\]+\/hls\/[^\s"'<>\\]*/gi,
|
|
483
|
+
];
|
|
484
|
+
for (const pattern of urlPatterns) {
|
|
485
|
+
let urlMatch;
|
|
486
|
+
while ((urlMatch = pattern.exec(unpacked)) !== null) {
|
|
487
|
+
if (!urls.includes(urlMatch[0])) {
|
|
488
|
+
urls.push(urlMatch[0]);
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
catch (e) {
|
|
495
|
+
// Skip failed unpacking
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
return { scripts, urls };
|
|
499
|
+
}
|
|
411
500
|
function applyDecodeLayer(input, layer) {
|
|
412
501
|
switch (layer) {
|
|
413
502
|
case 'base64': return decodeBase64(input);
|
|
@@ -415,6 +504,7 @@ function applyDecodeLayer(input, layer) {
|
|
|
415
504
|
case 'hex': return decodeHex(input);
|
|
416
505
|
case 'rot13': return decodeRot13(input);
|
|
417
506
|
case 'reverse': return reverseString(input);
|
|
507
|
+
case 'unpackJs': return unpackJs(input);
|
|
418
508
|
default: return input;
|
|
419
509
|
}
|
|
420
510
|
}
|
|
@@ -577,7 +667,48 @@ export async function handleExtractJson(page, args) {
|
|
|
577
667
|
}
|
|
578
668
|
}
|
|
579
669
|
// ============================================================
|
|
580
|
-
// 5.
|
|
670
|
+
// 5. DECODE PACKED JAVASCRIPT (AUTO-DETECT)
|
|
671
|
+
// ============================================================
|
|
672
|
+
if (args.decodePackedJs) {
|
|
673
|
+
try {
|
|
674
|
+
// Get all script contents from page
|
|
675
|
+
const scriptContents = await page.evaluate(() => {
|
|
676
|
+
const scripts = Array.from(document.querySelectorAll('script'));
|
|
677
|
+
return scripts.map(s => s.textContent || '').filter(s => s.length > 0);
|
|
678
|
+
});
|
|
679
|
+
const allUnpacked = { scripts: [], urls: [] };
|
|
680
|
+
for (const script of scriptContents) {
|
|
681
|
+
const result = extractAndUnpackAll(script);
|
|
682
|
+
allUnpacked.scripts.push(...result.scripts);
|
|
683
|
+
allUnpacked.urls.push(...result.urls);
|
|
684
|
+
}
|
|
685
|
+
// Remove duplicates from URLs
|
|
686
|
+
const uniqueUrls = [...new Set(allUnpacked.urls)];
|
|
687
|
+
if (allUnpacked.scripts.length > 0 || uniqueUrls.length > 0) {
|
|
688
|
+
decoded = {
|
|
689
|
+
source: 'decodePackedJs',
|
|
690
|
+
packedScriptsFound: allUnpacked.scripts.length,
|
|
691
|
+
unpackedScripts: allUnpacked.scripts.slice(0, 5), // Limit to first 5
|
|
692
|
+
extractedUrls: uniqueUrls,
|
|
693
|
+
streamUrls: uniqueUrls.filter(u => u.includes('m3u8') || u.includes('.txt?') ||
|
|
694
|
+
u.includes('/stream/') || u.includes('/hls/')),
|
|
695
|
+
};
|
|
696
|
+
data.push(decoded);
|
|
697
|
+
}
|
|
698
|
+
else {
|
|
699
|
+
decoded = {
|
|
700
|
+
source: 'decodePackedJs',
|
|
701
|
+
message: 'No packed JavaScript found in page',
|
|
702
|
+
hint: 'Try using execute_js or player_api_hook for dynamically loaded content',
|
|
703
|
+
};
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
catch (e) {
|
|
707
|
+
decoded = { source: 'decodePackedJs', error: String(e) };
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
// ============================================================
|
|
711
|
+
// 6. ORIGINAL JSON EXTRACTION (preserved)
|
|
581
712
|
// ============================================================
|
|
582
713
|
// Extract from script tags with type="application/json" or type="application/ld+json"
|
|
583
714
|
const scriptData = await page.evaluate((selector) => {
|
package/dist/tool-definitions.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Server metadata
|
|
3
3
|
export const SERVER_INFO = {
|
|
4
4
|
name: 'brave-real-browser-mcp-server',
|
|
5
|
-
version: '2.
|
|
5
|
+
version: '2.25.0',
|
|
6
6
|
};
|
|
7
7
|
// MCP capabilities with LSP-compatible streaming support
|
|
8
8
|
export const CAPABILITIES = {
|
|
@@ -470,7 +470,7 @@ 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.) + Advanced Decode capabilities for complex websites (base64, URL, hex, rot13, multi-layer)',
|
|
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) + Packed JavaScript decoder (eval/function(p,a,c,k,e,d) unpacker)',
|
|
474
474
|
inputSchema: {
|
|
475
475
|
type: 'object',
|
|
476
476
|
additionalProperties: false,
|
|
@@ -484,15 +484,17 @@ export const TOOLS = [
|
|
|
484
484
|
decodeFromUrl: { type: 'string', description: 'URL to extract and decode encoded parameter from (e.g., gadgetsweb.xyz/?id=xxx)' },
|
|
485
485
|
decodeUrlParam: { type: 'string', description: 'URL parameter name to decode (default: id)', default: 'id' },
|
|
486
486
|
decodePattern: { type: 'string', description: 'Regex pattern to find and decode base64 strings in page content' },
|
|
487
|
+
// NEW: Packed JavaScript decoder
|
|
488
|
+
decodePackedJs: { type: 'boolean', description: 'Auto-detect and decode packed JavaScript (eval/function(p,a,c,k,e,d)) in page scripts. Extracts m3u8/stream URLs automatically.', default: false },
|
|
487
489
|
advancedDecode: {
|
|
488
490
|
type: 'object',
|
|
489
|
-
description: 'Multi-layer decode for complex obfuscation (supports base64, url, hex, rot13, reverse)',
|
|
491
|
+
description: 'Multi-layer decode for complex obfuscation (supports base64, url, hex, rot13, reverse, unpackJs)',
|
|
490
492
|
properties: {
|
|
491
493
|
input: { type: 'string', description: 'Input string to decode (if not using page content)' },
|
|
492
494
|
layers: {
|
|
493
495
|
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
|
+
items: { type: 'string', enum: ['base64', 'url', 'hex', 'rot13', 'reverse', 'unpackJs'] },
|
|
497
|
+
description: 'Decode layers to apply in order (e.g., ["base64", "url", "unpackJs"])'
|
|
496
498
|
},
|
|
497
499
|
extractFromPage: { type: 'boolean', description: 'Extract input from current page content', default: false },
|
|
498
500
|
pageSelector: { type: 'string', description: 'CSS selector to extract from (for extractFromPage)' },
|
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.14",
|
|
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.14",
|
|
54
54
|
"puppeteer-core": "^24.35.0",
|
|
55
55
|
"turndown": "latest",
|
|
56
56
|
"vscode-languageserver": "^9.0.1",
|