brave-real-browser-mcp-server 2.16.0 ā 2.17.0
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/browser-manager.js +10 -1
- package/dist/extension-manager.js +135 -0
- package/package.json +1 -1
- package/scripts/verify-ublock.ts +58 -0
package/dist/browser-manager.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { connect } from 'brave-real-browser';
|
|
2
2
|
import * as fs from 'fs';
|
|
3
|
+
import { ExtensionManager } from './extension-manager.js';
|
|
3
4
|
import * as path from 'path';
|
|
4
5
|
import * as net from 'net';
|
|
5
6
|
import { execSync, spawn } from 'child_process';
|
|
@@ -455,6 +456,13 @@ export async function initializeBrowser(options) {
|
|
|
455
456
|
}
|
|
456
457
|
const customConfig = options?.customConfig ?? {};
|
|
457
458
|
const platform = process.platform;
|
|
459
|
+
// Auto-install/update uBlock Origin
|
|
460
|
+
const extensionPath = await ExtensionManager.ensureUBlockOrigin();
|
|
461
|
+
const uBlockArgs = [];
|
|
462
|
+
if (extensionPath) {
|
|
463
|
+
console.error(`š Loading uBlock Origin from: ${extensionPath}`);
|
|
464
|
+
uBlockArgs.push(`--disable-extensions-except=${extensionPath}`, `--load-extension=${extensionPath}`);
|
|
465
|
+
}
|
|
458
466
|
const getOptimalBraveFlags = (isWindows, isRetry = false) => {
|
|
459
467
|
// 2025 best practices: Minimal, secure, performance-focused flags
|
|
460
468
|
const baseFlags = [
|
|
@@ -544,7 +552,8 @@ export async function initializeBrowser(options) {
|
|
|
544
552
|
headless: options?.headless ?? (process.env.HEADLESS === 'true'),
|
|
545
553
|
turnstile: true,
|
|
546
554
|
args: [
|
|
547
|
-
"--start-maximized"
|
|
555
|
+
"--start-maximized",
|
|
556
|
+
...uBlockArgs // Add uBlock args to primary strategy
|
|
548
557
|
],
|
|
549
558
|
disableXvfb: true,
|
|
550
559
|
// CRITICAL: Must be false to allow brave-real-browser to process DEFAULT_FLAGS
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import axios from 'axios';
|
|
4
|
+
import { exec } from 'child_process';
|
|
5
|
+
import { promisify } from 'util';
|
|
6
|
+
const execAsync = promisify(exec);
|
|
7
|
+
const EXTENSIONS_DIR = path.resolve(process.cwd(), 'extensions');
|
|
8
|
+
const UBLOCK_DIR = path.join(EXTENSIONS_DIR, 'ublock');
|
|
9
|
+
export class ExtensionManager {
|
|
10
|
+
static GITHUB_API_URL = 'https://api.github.com/repos/gorhill/uBlock/releases/latest';
|
|
11
|
+
static FALLBACK_VERSION_URL = 'https://github.com/gorhill/uBlock/releases/download/1.68.1rc1/uBlock0_1.68.1rc1.chromium.zip';
|
|
12
|
+
/**
|
|
13
|
+
* Ensures uBlock Origin is installed and up-to-date.
|
|
14
|
+
* Returns the path to the unzipped extension.
|
|
15
|
+
*/
|
|
16
|
+
static async ensureUBlockOrigin() {
|
|
17
|
+
try {
|
|
18
|
+
if (!fs.existsSync(UBLOCK_DIR)) {
|
|
19
|
+
console.error('š [Auto-Update] uBlock Origin not found. Installing...');
|
|
20
|
+
await this.installUBlock();
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
// Optional: Check for updates (Skipped for now to avoid Github API rate limits on every startup,
|
|
24
|
+
// using existence check primarily. A real-world app might check once a day.)
|
|
25
|
+
// For now, if it exists, use it.
|
|
26
|
+
console.error('ā
[Auto-Update] uBlock Origin is already installed.');
|
|
27
|
+
}
|
|
28
|
+
return UBLOCK_DIR;
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
console.error('ā [Auto-Update] Failed to install/update uBlock:', error);
|
|
32
|
+
// Fallback: Return empty string or throw? If fails, browser opens without adblock.
|
|
33
|
+
// Better to log and continue without extension than crash.
|
|
34
|
+
return '';
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
static async installUBlock() {
|
|
38
|
+
// 1. Create directory
|
|
39
|
+
if (!fs.existsSync(EXTENSIONS_DIR)) {
|
|
40
|
+
fs.mkdirSync(EXTENSIONS_DIR, { recursive: true });
|
|
41
|
+
}
|
|
42
|
+
// 2. Get Download URL
|
|
43
|
+
let downloadUrl = this.FALLBACK_VERSION_URL;
|
|
44
|
+
try {
|
|
45
|
+
const release = await axios.get(this.GITHUB_API_URL);
|
|
46
|
+
const assets = release.data.assets;
|
|
47
|
+
const chromiumAsset = assets.find((a) => a.name.includes('.chromium.zip'));
|
|
48
|
+
if (chromiumAsset) {
|
|
49
|
+
downloadUrl = chromiumAsset.browser_download_url;
|
|
50
|
+
console.error(`ā¬ļø [Auto-Update] Found latest version: ${release.data.tag_name}`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
catch (e) {
|
|
54
|
+
console.warn('ā ļø [Auto-Update] Failed to fetch latest release from GitHub (Rate limit?), using fallback.');
|
|
55
|
+
}
|
|
56
|
+
// 3. Download Zip
|
|
57
|
+
const zipPath = path.join(EXTENSIONS_DIR, 'ublock.zip');
|
|
58
|
+
console.error(`ā¬ļø [Auto-Update] Downloading ${downloadUrl}...`);
|
|
59
|
+
const response = await axios({
|
|
60
|
+
method: 'GET',
|
|
61
|
+
url: downloadUrl,
|
|
62
|
+
responseType: 'stream',
|
|
63
|
+
});
|
|
64
|
+
const writer = fs.createWriteStream(zipPath);
|
|
65
|
+
response.data.pipe(writer);
|
|
66
|
+
await new Promise((resolve, reject) => {
|
|
67
|
+
writer.on('finish', resolve);
|
|
68
|
+
writer.on('error', reject);
|
|
69
|
+
});
|
|
70
|
+
// 4. Extract
|
|
71
|
+
console.error('š¦ [Auto-Update] Extracting uBlock Origin...');
|
|
72
|
+
// Clean previous install if any partial
|
|
73
|
+
if (fs.existsSync(UBLOCK_DIR)) {
|
|
74
|
+
fs.rmSync(UBLOCK_DIR, { recursive: true, force: true });
|
|
75
|
+
}
|
|
76
|
+
// Also clean potential extraction artifacts
|
|
77
|
+
const preChildren = fs.readdirSync(EXTENSIONS_DIR);
|
|
78
|
+
preChildren.forEach(c => {
|
|
79
|
+
if (c.startsWith('uBlock0') && c.endsWith('.chromium')) {
|
|
80
|
+
fs.rmSync(path.join(EXTENSIONS_DIR, c), { recursive: true, force: true });
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
await this.extractZip(zipPath, EXTENSIONS_DIR);
|
|
84
|
+
// Wait for file locks to release
|
|
85
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
86
|
+
// Rename extracted folder to 'ublock'
|
|
87
|
+
const children = fs.readdirSync(EXTENSIONS_DIR);
|
|
88
|
+
const extractedFolder = children.find(c => c.startsWith('uBlock0') && c.endsWith('.chromium'));
|
|
89
|
+
if (extractedFolder) {
|
|
90
|
+
const fullPath = path.join(EXTENSIONS_DIR, extractedFolder);
|
|
91
|
+
try {
|
|
92
|
+
fs.renameSync(fullPath, UBLOCK_DIR);
|
|
93
|
+
}
|
|
94
|
+
catch (renameError) {
|
|
95
|
+
console.warn('ā ļø [Auto-Update] Rename failed, trying copy...', renameError);
|
|
96
|
+
fs.cpSync(fullPath, UBLOCK_DIR, { recursive: true, force: true });
|
|
97
|
+
fs.rmSync(fullPath, { recursive: true, force: true });
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
// If extracted directly or structure is different, we might already be good or failed.
|
|
102
|
+
// Let's assume if ublock dir doesn't exist, something went wrong, OR unzip worked differently.
|
|
103
|
+
if (!fs.existsSync(UBLOCK_DIR)) {
|
|
104
|
+
// Try to handle case where it unzipped contents directly (unlikely for uBlock release)
|
|
105
|
+
// or maybe check if there is another folder.
|
|
106
|
+
console.warn('ā ļø [Auto-Update] setup: Could not determine extracted folder name. Checking extensions dir content.');
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
// 5. Cleanup
|
|
110
|
+
if (fs.existsSync(zipPath)) {
|
|
111
|
+
fs.unlinkSync(zipPath);
|
|
112
|
+
}
|
|
113
|
+
console.error('ā
[Auto-Update] uBlock Origin installed successfully!');
|
|
114
|
+
}
|
|
115
|
+
static async extractZip(zipPath, destDir) {
|
|
116
|
+
// Cross-platform unzip without adding dependencies
|
|
117
|
+
if (process.platform === 'win32') {
|
|
118
|
+
// Use PowerShell Expand-Archive
|
|
119
|
+
const command = `powershell -Command "Expand-Archive -Path '${zipPath}' -DestinationPath '${destDir}' -Force"`;
|
|
120
|
+
await execAsync(command);
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
// Use unzip command on Linux/Mac
|
|
124
|
+
try {
|
|
125
|
+
await execAsync(`unzip -o "${zipPath}" -d "${destDir}"`);
|
|
126
|
+
}
|
|
127
|
+
catch (e) {
|
|
128
|
+
// Fallback or specific error handling (e.g., install unzip?)
|
|
129
|
+
// Most distros have unzip. If not:
|
|
130
|
+
console.error('ā [Auto-Update] `unzip` command failed. Please install unzip (sudo apt install unzip).');
|
|
131
|
+
throw e;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "brave-real-browser-mcp-server",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.17.0",
|
|
4
4
|
"description": "Universal AI IDE MCP Server - Auto-detects and supports all AI IDEs (Claude Desktop, Cursor, Windsurf, Cline, Zed, VSCode, Qoder AI, etc.) with Brave browser automation",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
|
|
2
|
+
import { ExtensionManager } from '../src/extension-manager.js';
|
|
3
|
+
import { initializeBrowser, closeBrowser } from '../src/browser-manager.js';
|
|
4
|
+
import * as fs from 'fs';
|
|
5
|
+
import * as path from 'path';
|
|
6
|
+
|
|
7
|
+
async function verifyUBlock() {
|
|
8
|
+
console.log('š Starting uBlock Origin Verification...');
|
|
9
|
+
|
|
10
|
+
// 1. Verify Download & Extraction
|
|
11
|
+
console.log('\nSTEP 1: Checking ExtensionManager...');
|
|
12
|
+
const extPath = await ExtensionManager.ensureUBlockOrigin();
|
|
13
|
+
console.log(`š Extension Path: ${extPath}`);
|
|
14
|
+
|
|
15
|
+
if (!extPath || !fs.existsSync(extPath)) {
|
|
16
|
+
console.error('ā Failed: Extension path does not exist!');
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const manifestPath = path.join(extPath, 'manifest.json');
|
|
21
|
+
if (fs.existsSync(manifestPath)) {
|
|
22
|
+
console.log('ā
Manifest.json found! Extension extracted correctly.');
|
|
23
|
+
} else {
|
|
24
|
+
console.error('ā Failed: manifest.json missing in extension folder!');
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// 2. Verify Browser Launch with Extension
|
|
29
|
+
console.log('\nSTEP 2: Launching Browser with uBlock...');
|
|
30
|
+
try {
|
|
31
|
+
const { browser } = await initializeBrowser({
|
|
32
|
+
headless: false, // Must be false or 'new' for extensions usually, but let's test our default config
|
|
33
|
+
autoInstall: true
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// Check if arguments were passed
|
|
37
|
+
const spawnArgs = browser.process()?.spawnargs || [];
|
|
38
|
+
const loadArg = spawnArgs.find(arg => arg.includes('--load-extension'));
|
|
39
|
+
|
|
40
|
+
if (loadArg && loadArg.includes(extPath)) {
|
|
41
|
+
console.log('ā
Browser launched with --load-extension argument pointing to uBlock!');
|
|
42
|
+
} else {
|
|
43
|
+
console.error('ā Failed: Browser launched but --load-extension arg missing or incorrect.');
|
|
44
|
+
console.error('Spawn Args:', spawnArgs);
|
|
45
|
+
await closeBrowser();
|
|
46
|
+
process.exit(1);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
await closeBrowser();
|
|
50
|
+
console.log('\n⨠VERIFICATION SUCCESSFUL: uBlock Origin is integrated!');
|
|
51
|
+
|
|
52
|
+
} catch (error) {
|
|
53
|
+
console.error('ā Browser Launch Failed:', error);
|
|
54
|
+
process.exit(1);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
verifyUBlock();
|