brave-real-browser-mcp-server 2.16.0 → 2.17.1

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.
@@ -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.16.0",
3
+ "version": "2.17.1",
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();