brave-real-browser-mcp-server 2.11.6 → 2.11.7

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 CHANGED
@@ -14,19 +14,28 @@ Provides AI assistants with powerful, detection-resistant browser automation cap
14
14
  3. [Features](#features)
15
15
  4. [Prerequisites](#prerequisites)
16
16
  5. [Installation](#installation)
17
- 6. [Usage](#usage)
18
- - [With Claude Desktop](#with-claude-desktop)
17
+ 6. [Multi-Protocol Support](#-multi-protocol-support)
18
+ 7. [Usage](#usage)
19
+ - [Quick Configuration Reference](#-quick-configuration-reference)
20
+ - [With Claude Desktop](#with-claude-desktop-mcp-protocol)
19
21
  - [With Claude Code CLI](#with-claude-code-cli)
20
22
  - [With Cursor IDE](#with-cursor-ide)
21
- - [With Other AI Assistants](#with-other-ai-assistants)
22
- 7. [Available Tools](#available-tools)
23
- 8. [Advanced Features](#advanced-features)
24
- 9. [Configuration](#configuration)
25
- 10. [Troubleshooting](#troubleshooting)
26
- 11. [Development](#development)
27
- 12. [Testing](#testing)
28
- 13. [Contributing](#contributing)
29
- 14. [License](#license)
23
+ - [With Warp AI Terminal](#with-warp-ai-terminal)
24
+ - [With Windsurf IDE](#with-windsurf-ide-codeium)
25
+ - [With Continue.dev](#with-continuedev-vscode-extension)
26
+ - [With Cody AI](#with-cody-ai-sourcegraph)
27
+ - [With Zed Editor](#with-zed-editor-lsp-mode)
28
+ - [With VSCode](#with-vscode-via-lsp)
29
+ - [HTTP/WebSocket Mode](#httpwebsocket-mode-for-any-language)
30
+ - [Complete MCP IDE Compatibility Matrix](#-complete-mcp-ide-compatibility-matrix)
31
+ 8. [Available Tools](#available-tools)
32
+ 9. [Advanced Features](#advanced-features)
33
+ 10. [Configuration](#configuration)
34
+ 11. [Troubleshooting](#troubleshooting)
35
+ 12. [Development](#development)
36
+ 13. [Testing](#testing)
37
+ 14. [Contributing](#contributing)
38
+ 15. [License](#license)
30
39
 
31
40
  ## Quick Start for Beginners
32
41
 
@@ -193,9 +202,65 @@ npm run build
193
202
  npm run dev
194
203
  ```
195
204
 
205
+ ## 🚀 Multi-Protocol Support
206
+
207
+ This server now supports **three protocols** for maximum compatibility:
208
+
209
+ | Protocol | Best For | Setup Difficulty |
210
+ |----------|----------|-----------------|
211
+ | **MCP** | Claude Desktop, Cursor, Warp AI | Easy |
212
+ | **HTTP/WebSocket** | Any programming language | Easy |
213
+ | **LSP** | Zed, VSCode, Neovim | Medium |
214
+
215
+ ### Quick Protocol Selection
216
+
217
+ ```bash
218
+ # MCP mode (default) - For Claude Desktop, Cursor, Warp
219
+ npx brave-real-browser-mcp-server
220
+
221
+ # HTTP mode - For REST API / Any language
222
+ npx brave-real-browser-mcp-server --mode http --port 3000
223
+
224
+ # LSP mode - For Zed AI IDE, VSCode, Neovim
225
+ npx brave-real-browser-mcp-server --mode lsp
226
+ ```
227
+
228
+ 📖 **Complete Guide:** See [MULTI_PROTOCOL_GUIDE.md](./MULTI_PROTOCOL_GUIDE.md) for detailed setup instructions.
229
+
230
+ ---
231
+
196
232
  ## Usage
197
233
 
198
- ### With Claude Desktop
234
+ ### 📝 Quick Configuration Reference
235
+
236
+ | Your IDE/Tool | Jump To Section |
237
+ |---------------|----------------|
238
+ | Claude Desktop | [Configuration](#with-claude-desktop-mcp-protocol) |
239
+ | Claude Code CLI | [Configuration](#with-claude-code-cli) |
240
+ | Cursor IDE | [Configuration](#with-cursor-ide) |
241
+ | Warp Terminal | [Configuration](#with-warp-ai-terminal) |
242
+ | Windsurf IDE | [Configuration](#with-windsurf-ide-codeium) |
243
+ | Continue.dev | [Configuration](#with-continuedev-vscode-extension) |
244
+ | Cody AI | [Configuration](#with-cody-ai-sourcegraph) |
245
+ | Zed Editor | [Configuration](#with-zed-editor-lsp-mode) |
246
+ | VSCode | [Configuration](#with-vscode-via-lsp) |
247
+ | Python/Node.js/Any Language | [Configuration](#httpwebsocket-mode-for-any-language) |
248
+
249
+ **All platforms use the same simple pattern:**
250
+ ```json
251
+ {
252
+ "mcpServers": {
253
+ "brave-real-browser": {
254
+ "command": "npx",
255
+ "args": ["brave-real-browser-mcp-server@latest"]
256
+ }
257
+ }
258
+ }
259
+ ```
260
+
261
+ ---
262
+
263
+ ### With Claude Desktop (MCP Protocol)
199
264
 
200
265
  The configuration below uses `npx` to automatically download and run the latest version. No installation required!
201
266
 
@@ -436,22 +501,207 @@ If successful, you should see:
436
501
  - Check file is in correct directory
437
502
  - Restart Cursor IDE after changes
438
503
 
439
- ### With Other AI Assistants
504
+ ### With Warp AI Terminal
440
505
 
441
- Start the server:
506
+ Warp Terminal has native MCP support built-in.
442
507
 
443
- ```bash
444
- brave-real-browser-mcp-server
508
+ **Configuration Location:**
509
+ - macOS: `~/.warp/mcp_config.json`
510
+ - Linux: `~/.warp/mcp_config.json`
511
+ - Windows: `%USERPROFILE%\.warp\mcp_config.json`
512
+
513
+ **Configuration:**
514
+ ```json
515
+ {
516
+ "mcpServers": {
517
+ "brave-real-browser": {
518
+ "command": "npx",
519
+ "args": ["brave-real-browser-mcp-server@latest"]
520
+ }
521
+ }
522
+ }
523
+ ```
524
+
525
+ **Testing:**
526
+ 1. Restart Warp Terminal
527
+ 2. Type: `/mcp` to see available servers
528
+ 3. Test: "Initialize browser and go to google.com"
529
+
530
+ ### With Windsurf IDE (Codeium)
531
+
532
+ Windsurf IDE by Codeium supports MCP servers.
533
+
534
+ **Configuration Location:**
535
+ - Create `.windsurf/mcp.json` in your project
536
+ - Or `~/.windsurf/mcp.json` for global configuration
537
+
538
+ **Configuration:**
539
+ ```json
540
+ {
541
+ "mcpServers": {
542
+ "brave-real-browser": {
543
+ "command": "npx",
544
+ "args": ["brave-real-browser-mcp-server@latest"],
545
+ "disabled": false
546
+ }
547
+ }
548
+ }
549
+ ```
550
+
551
+ ### With Continue.dev (VSCode Extension)
552
+
553
+ Continue.dev supports MCP through its VSCode/JetBrains extension.
554
+
555
+ **Configuration Location:**
556
+ - `~/.continue/config.json` (Global)
557
+ - Or `.continue/config.json` in your project
558
+
559
+ **Configuration:**
560
+ ```json
561
+ {
562
+ "mcpServers": [
563
+ {
564
+ "name": "brave-real-browser",
565
+ "command": "npx",
566
+ "args": ["brave-real-browser-mcp-server@latest"]
567
+ }
568
+ ]
569
+ }
445
570
  ```
446
571
 
447
- Or if installed from source:
572
+ ### With Cody AI (Sourcegraph)
448
573
 
574
+ Cody AI in VS Code supports MCP servers.
575
+
576
+ **Configuration Location:**
577
+ - VS Code Settings: `settings.json`
578
+
579
+ **Configuration:**
580
+ ```json
581
+ {
582
+ "cody.mcp.servers": {
583
+ "brave-real-browser": {
584
+ "command": "npx",
585
+ "args": ["brave-real-browser-mcp-server@latest"]
586
+ }
587
+ }
588
+ }
589
+ ```
590
+
591
+ ### With Zed Editor (LSP Mode)
592
+
593
+ Zed uses LSP protocol instead of MCP.
594
+
595
+ **Configuration Location:**
596
+ - macOS/Linux: `~/.config/zed/settings.json`
597
+ - Windows: `%APPDATA%\Zed\settings.json`
598
+
599
+ **Configuration:**
600
+ ```json
601
+ {
602
+ "lsp": {
603
+ "brave-browser-automation": {
604
+ "command": "npx",
605
+ "args": ["brave-real-browser-mcp-server@latest", "--mode", "lsp"],
606
+ "settings": {}
607
+ }
608
+ }
609
+ }
610
+ ```
611
+
612
+ ### With VSCode (via LSP)
613
+
614
+ VSCode can use LSP mode for browser automation.
615
+
616
+ **Create:** `.vscode/settings.json` in your project
617
+
618
+ **Configuration:**
619
+ ```json
620
+ {
621
+ "brave-browser-automation.serverPath": "npx",
622
+ "brave-browser-automation.serverArgs": [
623
+ "brave-real-browser-mcp-server@latest",
624
+ "--mode",
625
+ "lsp"
626
+ ]
627
+ }
628
+ ```
629
+
630
+ ### HTTP/WebSocket Mode (For Any Language)
631
+
632
+ Use REST API mode for maximum compatibility with any programming language.
633
+
634
+ **Start Server:**
449
635
  ```bash
450
- npm start
636
+ npx brave-real-browser-mcp-server --mode http --port 3000
637
+ ```
638
+
639
+ **Example - Python:**
640
+ ```python
641
+ import requests
642
+
643
+ base_url = "http://localhost:3000"
644
+
645
+ # Initialize browser
646
+ requests.post(f"{base_url}/browser/init", json={"headless": False})
647
+
648
+ # Navigate
649
+ requests.post(f"{base_url}/browser/navigate", json={"url": "https://google.com"})
650
+
651
+ # Get content
652
+ response = requests.post(f"{base_url}/browser/get-content", json={"type": "text"})
653
+ print(response.json())
654
+ ```
655
+
656
+ **Example - Node.js:**
657
+ ```javascript
658
+ const axios = require('axios');
659
+
660
+ const baseUrl = 'http://localhost:3000';
661
+
662
+ // Initialize and automate
663
+ await axios.post(`${baseUrl}/browser/init`, {headless: false});
664
+ await axios.post(`${baseUrl}/browser/navigate`, {url: 'https://google.com'});
665
+ const content = await axios.post(`${baseUrl}/browser/get-content`, {type: 'text'});
666
+ console.log(content.data);
667
+ ```
668
+
669
+ ### With Other MCP-Compatible Tools
670
+
671
+ For any other MCP-compatible IDE or tool, use the standard MCP configuration:
672
+
673
+ ```json
674
+ {
675
+ "mcpServers": {
676
+ "brave-real-browser": {
677
+ "command": "npx",
678
+ "args": ["brave-real-browser-mcp-server@latest"]
679
+ }
680
+ }
681
+ }
451
682
  ```
452
683
 
453
684
  The server communicates via stdin/stdout using the MCP protocol.
454
685
 
686
+ ---
687
+
688
+ ## 📊 Complete MCP IDE Compatibility Matrix
689
+
690
+ | IDE/Tool | Protocol | Config File | Status |
691
+ |----------|----------|-------------|--------|
692
+ | **Claude Desktop** | MCP | `claude_desktop_config.json` | ✅ Fully Tested |
693
+ | **Claude Code CLI** | MCP | `.mcp.json` | ✅ Fully Tested |
694
+ | **Cursor IDE** | MCP | `.cursor/mcp.json` | ✅ Fully Tested |
695
+ | **Warp Terminal** | MCP | `mcp_config.json` | ✅ Supported |
696
+ | **Windsurf IDE** | MCP | `.windsurf/mcp.json` | ✅ Supported |
697
+ | **Continue.dev** | MCP | `.continue/config.json` | ✅ Supported |
698
+ | **Cody AI** | MCP | `settings.json` | ✅ Supported |
699
+ | **Zed Editor** | LSP | `settings.json` | ✅ LSP Mode |
700
+ | **VSCode** | LSP | `.vscode/settings.json` | ✅ LSP Mode |
701
+ | **Any HTTP Client** | HTTP/WebSocket | REST API | ✅ Fully Supported |
702
+
703
+ **Total Supported Platforms:** 10+
704
+
455
705
  ### Example Interactions
456
706
 
457
707
  #### Basic Web Browsing
package/dist/index.js CHANGED
@@ -483,42 +483,30 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
483
483
  };
484
484
  }
485
485
  });
486
- // Main function to start the server
486
+ // Main function - now using multi-protocol launcher
487
+ import { main as launcherMain } from './launcher.js';
487
488
  async function main() {
488
- console.error('🔍 [DEBUG] Main function starting...');
489
- // Setup process cleanup handlers
490
- console.error('🔍 [DEBUG] Setting up process cleanup handlers...');
491
- setupProcessCleanup(async () => {
492
- console.error('🔍 [DEBUG] Process cleanup triggered');
493
- await closeBrowser();
494
- await forceKillAllBraveProcesses();
495
- });
496
- // Create and start the server transport
497
- console.error('🔍 [DEBUG] Creating StdioServerTransport...');
498
- const transport = new StdioServerTransport();
499
- console.error('🔍 [DEBUG] StdioServerTransport created successfully');
500
- await withErrorHandling(async () => {
501
- console.error('🔍 [DEBUG] Attempting to connect server to transport...');
502
- await server.connect(transport);
503
- console.error('🔍 [DEBUG] Server connected to transport successfully');
504
- console.error('🚀 Brave Real Browser MCP Server started successfully');
505
- console.error('📋 Available tools:', TOOLS.map(t => t.name).join(', '));
506
- console.error('🔧 Workflow validation: Active');
507
- console.error('💡 Content priority mode: Enabled (use get_content for better reliability)');
508
- console.error('🔍 [DEBUG] Server is now ready and waiting for requests...');
509
- // Keep the process alive by maintaining the connection
510
- console.error('🔍 [DEBUG] Maintaining process alive - server will wait for requests');
511
- // Add a heartbeat to confirm the process is still running
512
- const heartbeat = setInterval(() => {
513
- console.error(`🔍 [DEBUG] Heartbeat - Server alive at ${new Date().toISOString()}`);
514
- }, 30000); // Every 30 seconds
515
- // Cleanup heartbeat on process exit
516
- process.on('exit', () => {
517
- console.error('🔍 [DEBUG] Process exiting - clearing heartbeat');
518
- clearInterval(heartbeat);
489
+ // Check if user wants multi-protocol mode
490
+ const hasProtocolArg = process.argv.some(arg => arg === '--mode' || arg === '-m' || arg === '--http' || arg === '--lsp');
491
+ if (hasProtocolArg) {
492
+ // Use multi-protocol launcher
493
+ await launcherMain();
494
+ }
495
+ else {
496
+ // Default: MCP mode (backward compatibility)
497
+ console.error('🔍 [DEBUG] Starting in MCP mode (default)...');
498
+ console.error('💡 Tip: Use --mode http or --mode lsp for other protocols');
499
+ setupProcessCleanup(async () => {
500
+ await closeBrowser();
501
+ await forceKillAllBraveProcesses();
519
502
  });
520
- }, 'Failed to start MCP server');
521
- console.error('🔍 [DEBUG] Main function completed - server should be running');
503
+ const transport = new StdioServerTransport();
504
+ await withErrorHandling(async () => {
505
+ await server.connect(transport);
506
+ console.error('🚀 Brave Real Browser MCP Server started successfully');
507
+ console.error('📋 Available tools:', TOOLS.map(t => t.name).join(', '));
508
+ }, 'Failed to start MCP server');
509
+ }
522
510
  }
523
511
  // Enhanced error handling with debug info
524
512
  console.error('🔍 [DEBUG] Setting up error handlers...');
@@ -0,0 +1,164 @@
1
+ #!/usr/bin/env node
2
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
3
+ import { HttpTransport } from './transports/http-transport.js';
4
+ import { LspTransport } from './transports/lsp-transport.js';
5
+ import { closeBrowser, forceKillAllBraveProcesses } from './browser-manager.js';
6
+ import { setupProcessCleanup } from './core-infrastructure.js';
7
+ export class MultiProtocolLauncher {
8
+ config;
9
+ httpTransport;
10
+ lspTransport;
11
+ mcpServer;
12
+ constructor(config = {}) {
13
+ this.config = {
14
+ mode: config.mode || 'mcp',
15
+ httpPort: config.httpPort || 3000,
16
+ httpHost: config.httpHost || '0.0.0.0',
17
+ enableWebSocket: config.enableWebSocket !== false,
18
+ enableCors: config.enableCors !== false,
19
+ };
20
+ }
21
+ async start() {
22
+ console.error('🚀 Multi-Protocol Brave Browser Server Starting...');
23
+ console.error(`📡 Mode: ${this.config.mode.toUpperCase()}`);
24
+ // Setup cleanup handlers
25
+ setupProcessCleanup(async () => {
26
+ await this.stop();
27
+ });
28
+ switch (this.config.mode) {
29
+ case 'mcp':
30
+ await this.startMcp();
31
+ break;
32
+ case 'http':
33
+ await this.startHttp();
34
+ break;
35
+ case 'lsp':
36
+ await this.startLsp();
37
+ break;
38
+ case 'all':
39
+ await this.startAll();
40
+ break;
41
+ default:
42
+ throw new Error(`Unknown mode: ${this.config.mode}`);
43
+ }
44
+ }
45
+ async startMcp() {
46
+ console.error('🔵 [MCP] Starting MCP server...');
47
+ // Import MCP server setup from index.ts
48
+ const { createMcpServer } = await import('./mcp-server.js');
49
+ this.mcpServer = await createMcpServer();
50
+ const transport = new StdioServerTransport();
51
+ await this.mcpServer.connect(transport);
52
+ console.error('✅ [MCP] Server started successfully');
53
+ }
54
+ async startHttp() {
55
+ console.error('🟢 [HTTP] Starting HTTP/WebSocket server...');
56
+ this.httpTransport = new HttpTransport({
57
+ port: this.config.httpPort,
58
+ host: this.config.httpHost,
59
+ enableWebSocket: this.config.enableWebSocket,
60
+ });
61
+ await this.httpTransport.start();
62
+ }
63
+ async startLsp() {
64
+ console.error('🟣 [LSP] Starting Language Server...');
65
+ this.lspTransport = new LspTransport();
66
+ await this.lspTransport.start();
67
+ }
68
+ async startAll() {
69
+ console.error('🌈 Starting all protocols...');
70
+ // Start HTTP in background
71
+ if (!this.httpTransport) {
72
+ this.httpTransport = new HttpTransport({
73
+ port: this.config.httpPort,
74
+ host: this.config.httpHost,
75
+ enableWebSocket: this.config.enableWebSocket,
76
+ });
77
+ await this.httpTransport.start();
78
+ }
79
+ // Note: LSP and MCP use stdio/IPC, so they can't run simultaneously
80
+ // In 'all' mode, we prioritize HTTP/WebSocket for universal access
81
+ console.error('⚠️ [Note] MCP and LSP use stdio, so only HTTP/WebSocket is active in "all" mode');
82
+ console.error('💡 Use separate instances for MCP or LSP: --mode=mcp or --mode=lsp');
83
+ }
84
+ async stop() {
85
+ console.error('🛑 Stopping servers...');
86
+ await closeBrowser();
87
+ await forceKillAllBraveProcesses();
88
+ if (this.httpTransport) {
89
+ await this.httpTransport.stop();
90
+ }
91
+ if (this.lspTransport) {
92
+ await this.lspTransport.stop();
93
+ }
94
+ console.error('✅ All servers stopped');
95
+ }
96
+ }
97
+ // Parse CLI arguments
98
+ export function parseArgs() {
99
+ const args = process.argv.slice(2);
100
+ const config = {};
101
+ for (let i = 0; i < args.length; i++) {
102
+ const arg = args[i];
103
+ if (arg === '--mode' || arg === '-m') {
104
+ const mode = args[++i];
105
+ if (['mcp', 'http', 'lsp', 'all'].includes(mode)) {
106
+ config.mode = mode;
107
+ }
108
+ else {
109
+ console.error(`❌ Invalid mode: ${mode}. Use: mcp, http, lsp, or all`);
110
+ process.exit(1);
111
+ }
112
+ }
113
+ else if (arg === '--port' || arg === '-p') {
114
+ config.httpPort = parseInt(args[++i]);
115
+ }
116
+ else if (arg === '--host' || arg === '-h') {
117
+ config.httpHost = args[++i];
118
+ }
119
+ else if (arg === '--no-websocket') {
120
+ config.enableWebSocket = false;
121
+ }
122
+ else if (arg === '--help') {
123
+ console.log(`
124
+ Brave Real Browser MCP Server - Multi-Protocol Support
125
+
126
+ Usage: brave-real-browser-mcp-server [options]
127
+
128
+ Options:
129
+ --mode, -m <mode> Protocol mode: mcp, http, lsp, or all (default: mcp)
130
+ --port, -p <port> HTTP server port (default: 3000)
131
+ --host, -h <host> HTTP server host (default: 0.0.0.0)
132
+ --no-websocket Disable WebSocket support in HTTP mode
133
+ --help Show this help message
134
+
135
+ Examples:
136
+ # MCP mode (for Claude Desktop, Cursor, Warp)
137
+ brave-real-browser-mcp-server --mode mcp
138
+
139
+ # HTTP/WebSocket mode (for any HTTP client)
140
+ brave-real-browser-mcp-server --mode http --port 3000
141
+
142
+ # LSP mode (for Zed, VSCode, etc.)
143
+ brave-real-browser-mcp-server --mode lsp
144
+
145
+ # All protocols (HTTP only, MCP/LSP need separate instances)
146
+ brave-real-browser-mcp-server --mode all
147
+ `);
148
+ process.exit(0);
149
+ }
150
+ }
151
+ return config;
152
+ }
153
+ // Main entry point
154
+ export async function main() {
155
+ const config = parseArgs();
156
+ const launcher = new MultiProtocolLauncher(config);
157
+ try {
158
+ await launcher.start();
159
+ }
160
+ catch (error) {
161
+ console.error('❌ Failed to start server:', error);
162
+ process.exit(1);
163
+ }
164
+ }
@@ -0,0 +1,141 @@
1
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
2
+ import { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSchema, ListPromptsRequestSchema, InitializeRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
3
+ import { TOOLS, SERVER_INFO, CAPABILITIES, TOOL_NAMES } from './tool-definitions.js';
4
+ import { validateMCPResponse } from './mcp-response-validator.js';
5
+ // Import handlers
6
+ import { handleBrowserInit, handleBrowserClose } from './handlers/browser-handlers.js';
7
+ import { handleNavigate, handleWait } from './handlers/navigation-handlers.js';
8
+ import { handleClick, handleType, handleSolveCaptcha, handleRandomScroll } from './handlers/interaction-handlers.js';
9
+ import { handleGetContent, handleFindSelector } from './handlers/content-handlers.js';
10
+ import { handleSaveContentAsMarkdown } from './handlers/file-handlers.js';
11
+ import { handleScrapeTable, handleExtractList, handleExtractJSON, handleScrapeMetaTags, handleExtractSchema } from './handlers/data-extraction-handlers.js';
12
+ import { handleBatchElementScraper, handleNestedDataExtraction, handleAttributeHarvester, handleImageScraper, handleLinkHarvester, handleMediaExtractor, handlePDFLinkFinder } from './handlers/multi-element-handlers.js';
13
+ export async function createMcpServer() {
14
+ const server = new Server(SERVER_INFO, { capabilities: CAPABILITIES });
15
+ // Register initialize handler
16
+ server.setRequestHandler(InitializeRequestSchema, async (request) => {
17
+ const clientProtocolVersion = request.params.protocolVersion;
18
+ return {
19
+ protocolVersion: clientProtocolVersion,
20
+ capabilities: CAPABILITIES,
21
+ serverInfo: SERVER_INFO,
22
+ };
23
+ });
24
+ // Register tool handlers
25
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
26
+ return { tools: TOOLS };
27
+ });
28
+ // Register resource handlers (placeholder)
29
+ server.setRequestHandler(ListResourcesRequestSchema, async () => {
30
+ return { resources: [] };
31
+ });
32
+ // Register prompt handlers (placeholder)
33
+ server.setRequestHandler(ListPromptsRequestSchema, async () => {
34
+ return { prompts: [] };
35
+ });
36
+ // Main tool call handler
37
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
38
+ const { name, arguments: args } = request.params;
39
+ try {
40
+ let result;
41
+ switch (name) {
42
+ case TOOL_NAMES.BROWSER_INIT:
43
+ result = await handleBrowserInit(args || {});
44
+ break;
45
+ case TOOL_NAMES.NAVIGATE:
46
+ result = await handleNavigate(args);
47
+ break;
48
+ case TOOL_NAMES.GET_CONTENT:
49
+ result = await handleGetContent(args || {});
50
+ break;
51
+ case TOOL_NAMES.CLICK:
52
+ result = await handleClick(args);
53
+ break;
54
+ case TOOL_NAMES.TYPE:
55
+ result = await handleType(args);
56
+ break;
57
+ case TOOL_NAMES.WAIT:
58
+ result = await handleWait(args);
59
+ break;
60
+ case TOOL_NAMES.BROWSER_CLOSE:
61
+ result = await handleBrowserClose();
62
+ break;
63
+ case TOOL_NAMES.SOLVE_CAPTCHA:
64
+ result = await handleSolveCaptcha(args);
65
+ break;
66
+ case TOOL_NAMES.RANDOM_SCROLL:
67
+ result = await handleRandomScroll();
68
+ break;
69
+ case TOOL_NAMES.FIND_SELECTOR:
70
+ result = await handleFindSelector(args);
71
+ break;
72
+ case TOOL_NAMES.SAVE_CONTENT_AS_MARKDOWN:
73
+ result = await handleSaveContentAsMarkdown(args);
74
+ break;
75
+ // Smart Data Extractors
76
+ case TOOL_NAMES.SCRAPE_TABLE:
77
+ result = await handleScrapeTable(args || {});
78
+ break;
79
+ case TOOL_NAMES.EXTRACT_LIST:
80
+ result = await handleExtractList(args || {});
81
+ break;
82
+ case TOOL_NAMES.EXTRACT_JSON:
83
+ result = await handleExtractJSON(args || {});
84
+ break;
85
+ case TOOL_NAMES.SCRAPE_META_TAGS:
86
+ result = await handleScrapeMetaTags(args || {});
87
+ break;
88
+ case TOOL_NAMES.EXTRACT_SCHEMA:
89
+ result = await handleExtractSchema(args || {});
90
+ break;
91
+ // Multi-Element Extractors
92
+ case TOOL_NAMES.BATCH_ELEMENT_SCRAPER:
93
+ result = await handleBatchElementScraper(args);
94
+ break;
95
+ case TOOL_NAMES.NESTED_DATA_EXTRACTION:
96
+ result = await handleNestedDataExtraction(args);
97
+ break;
98
+ case TOOL_NAMES.ATTRIBUTE_HARVESTER:
99
+ result = await handleAttributeHarvester(args);
100
+ break;
101
+ // Content Type Specific
102
+ case TOOL_NAMES.IMAGE_SCRAPER:
103
+ result = await handleImageScraper(args || {});
104
+ break;
105
+ case TOOL_NAMES.LINK_HARVESTER:
106
+ result = await handleLinkHarvester(args || {});
107
+ break;
108
+ case TOOL_NAMES.MEDIA_EXTRACTOR:
109
+ result = await handleMediaExtractor(args || {});
110
+ break;
111
+ case TOOL_NAMES.PDF_LINK_FINDER:
112
+ result = await handlePDFLinkFinder(args || {});
113
+ break;
114
+ default:
115
+ throw new Error(`Unknown tool: ${name}`);
116
+ }
117
+ // Validate MCP response format universally
118
+ return validateMCPResponse(result, name);
119
+ }
120
+ catch (error) {
121
+ const errorMessage = error instanceof Error ? error.message : String(error);
122
+ // For workflow validation errors, throw them so MCP SDK handles them properly
123
+ if (errorMessage.includes('cannot be executed in current state') ||
124
+ errorMessage.includes('Cannot search for selectors') ||
125
+ errorMessage.includes('Next Steps:')) {
126
+ throw error;
127
+ }
128
+ // For other errors, return formatted response
129
+ return {
130
+ content: [
131
+ {
132
+ type: 'text',
133
+ text: `❌ Tool execution failed: ${errorMessage}`,
134
+ },
135
+ ],
136
+ isError: true,
137
+ };
138
+ }
139
+ });
140
+ return server;
141
+ }
@@ -0,0 +1,222 @@
1
+ import express from 'express';
2
+ import { createServer } from 'http';
3
+ import { WebSocketServer } from 'ws';
4
+ import { TOOLS, TOOL_NAMES } from '../tool-definitions.js';
5
+ // Import all handlers
6
+ import { handleBrowserInit, handleBrowserClose } from '../handlers/browser-handlers.js';
7
+ import { handleNavigate, handleWait } from '../handlers/navigation-handlers.js';
8
+ import { handleClick, handleType, handleSolveCaptcha, handleRandomScroll } from '../handlers/interaction-handlers.js';
9
+ import { handleGetContent, handleFindSelector } from '../handlers/content-handlers.js';
10
+ import { handleSaveContentAsMarkdown } from '../handlers/file-handlers.js';
11
+ export class HttpTransport {
12
+ app;
13
+ server = null;
14
+ wss = null;
15
+ config;
16
+ constructor(config = {}) {
17
+ this.config = {
18
+ port: config.port || 3000,
19
+ host: config.host || '0.0.0.0',
20
+ enableWebSocket: config.enableWebSocket !== false,
21
+ corsOrigins: config.corsOrigins || ['*'],
22
+ };
23
+ this.app = express();
24
+ this.setupMiddleware();
25
+ this.setupRoutes();
26
+ }
27
+ setupMiddleware() {
28
+ // Body parsing
29
+ this.app.use(express.json());
30
+ this.app.use(express.urlencoded({ extended: true }));
31
+ // CORS
32
+ this.app.use((req, res, next) => {
33
+ res.header('Access-Control-Allow-Origin', this.config.corsOrigins?.[0] || '*');
34
+ res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
35
+ res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
36
+ if (req.method === 'OPTIONS') {
37
+ return res.sendStatus(200);
38
+ }
39
+ next();
40
+ });
41
+ // Request logging
42
+ this.app.use((req, res, next) => {
43
+ console.error(`📡 [HTTP] ${req.method} ${req.path} - ${new Date().toISOString()}`);
44
+ next();
45
+ });
46
+ }
47
+ setupRoutes() {
48
+ // Health check
49
+ this.app.get('/health', (req, res) => {
50
+ res.json({ status: 'ok', timestamp: new Date().toISOString() });
51
+ });
52
+ // List all available tools
53
+ this.app.get('/tools', (req, res) => {
54
+ res.json({ tools: TOOLS });
55
+ });
56
+ // Execute tool - Generic endpoint
57
+ this.app.post('/tools/:toolName', async (req, res) => {
58
+ const { toolName } = req.params;
59
+ const args = req.body;
60
+ try {
61
+ const result = await this.executeTool(toolName, args);
62
+ res.json({ success: true, result });
63
+ }
64
+ catch (error) {
65
+ const errorMessage = error instanceof Error ? error.message : String(error);
66
+ res.status(500).json({ success: false, error: errorMessage });
67
+ }
68
+ });
69
+ // Browser automation endpoints
70
+ this.app.post('/browser/init', async (req, res) => {
71
+ try {
72
+ const result = await handleBrowserInit(req.body);
73
+ res.json({ success: true, result });
74
+ }
75
+ catch (error) {
76
+ res.status(500).json({ success: false, error: error.message });
77
+ }
78
+ });
79
+ this.app.post('/browser/navigate', async (req, res) => {
80
+ try {
81
+ const result = await handleNavigate(req.body);
82
+ res.json({ success: true, result });
83
+ }
84
+ catch (error) {
85
+ res.status(500).json({ success: false, error: error.message });
86
+ }
87
+ });
88
+ this.app.post('/browser/click', async (req, res) => {
89
+ try {
90
+ const result = await handleClick(req.body);
91
+ res.json({ success: true, result });
92
+ }
93
+ catch (error) {
94
+ res.status(500).json({ success: false, error: error.message });
95
+ }
96
+ });
97
+ this.app.post('/browser/type', async (req, res) => {
98
+ try {
99
+ const result = await handleType(req.body);
100
+ res.json({ success: true, result });
101
+ }
102
+ catch (error) {
103
+ res.status(500).json({ success: false, error: error.message });
104
+ }
105
+ });
106
+ this.app.post('/browser/get-content', async (req, res) => {
107
+ try {
108
+ const result = await handleGetContent(req.body);
109
+ res.json({ success: true, result });
110
+ }
111
+ catch (error) {
112
+ res.status(500).json({ success: false, error: error.message });
113
+ }
114
+ });
115
+ this.app.post('/browser/close', async (req, res) => {
116
+ try {
117
+ const result = await handleBrowserClose();
118
+ res.json({ success: true, result });
119
+ }
120
+ catch (error) {
121
+ res.status(500).json({ success: false, error: error.message });
122
+ }
123
+ });
124
+ // Error handling
125
+ this.app.use((err, req, res, next) => {
126
+ console.error('❌ [HTTP] Error:', err);
127
+ res.status(500).json({ success: false, error: err.message });
128
+ });
129
+ }
130
+ async executeTool(toolName, args) {
131
+ switch (toolName) {
132
+ case TOOL_NAMES.BROWSER_INIT:
133
+ return await handleBrowserInit(args || {});
134
+ case TOOL_NAMES.NAVIGATE:
135
+ return await handleNavigate(args);
136
+ case TOOL_NAMES.GET_CONTENT:
137
+ return await handleGetContent(args || {});
138
+ case TOOL_NAMES.CLICK:
139
+ return await handleClick(args);
140
+ case TOOL_NAMES.TYPE:
141
+ return await handleType(args);
142
+ case TOOL_NAMES.WAIT:
143
+ return await handleWait(args);
144
+ case TOOL_NAMES.BROWSER_CLOSE:
145
+ return await handleBrowserClose();
146
+ case TOOL_NAMES.SOLVE_CAPTCHA:
147
+ return await handleSolveCaptcha(args);
148
+ case TOOL_NAMES.RANDOM_SCROLL:
149
+ return await handleRandomScroll();
150
+ case TOOL_NAMES.FIND_SELECTOR:
151
+ return await handleFindSelector(args);
152
+ case TOOL_NAMES.SAVE_CONTENT_AS_MARKDOWN:
153
+ return await handleSaveContentAsMarkdown(args);
154
+ default:
155
+ throw new Error(`Unknown tool: ${toolName}`);
156
+ }
157
+ }
158
+ setupWebSocket() {
159
+ if (!this.server || !this.config.enableWebSocket)
160
+ return;
161
+ this.wss = new WebSocketServer({ server: this.server });
162
+ this.wss.on('connection', (ws) => {
163
+ console.error('🔌 [WebSocket] Client connected');
164
+ ws.on('message', async (message) => {
165
+ try {
166
+ const data = JSON.parse(message.toString());
167
+ const { id, tool, args } = data;
168
+ const result = await this.executeTool(tool, args);
169
+ ws.send(JSON.stringify({
170
+ id,
171
+ success: true,
172
+ result,
173
+ }));
174
+ }
175
+ catch (error) {
176
+ const errorMessage = error instanceof Error ? error.message : String(error);
177
+ ws.send(JSON.stringify({
178
+ success: false,
179
+ error: errorMessage,
180
+ }));
181
+ }
182
+ });
183
+ ws.on('close', () => {
184
+ console.error('🔌 [WebSocket] Client disconnected');
185
+ });
186
+ ws.on('error', (error) => {
187
+ console.error('❌ [WebSocket] Error:', error);
188
+ });
189
+ });
190
+ }
191
+ async start() {
192
+ return new Promise((resolve) => {
193
+ this.server = createServer(this.app);
194
+ this.setupWebSocket();
195
+ this.server.listen(this.config.port, this.config.host, () => {
196
+ console.error(`✅ [HTTP] Server running on http://${this.config.host}:${this.config.port}`);
197
+ if (this.config.enableWebSocket) {
198
+ console.error(`✅ [WebSocket] Server running on ws://${this.config.host}:${this.config.port}`);
199
+ }
200
+ resolve();
201
+ });
202
+ });
203
+ }
204
+ async stop() {
205
+ return new Promise((resolve, reject) => {
206
+ if (this.wss) {
207
+ this.wss.close();
208
+ }
209
+ if (this.server) {
210
+ this.server.close((err) => {
211
+ if (err)
212
+ reject(err);
213
+ else
214
+ resolve();
215
+ });
216
+ }
217
+ else {
218
+ resolve();
219
+ }
220
+ });
221
+ }
222
+ }
@@ -0,0 +1,165 @@
1
+ import { createConnection, TextDocuments, ProposedFeatures, CompletionItemKind, TextDocumentSyncKind, } from 'vscode-languageserver/node.js';
2
+ import { TextDocument } from 'vscode-languageserver-textdocument';
3
+ import { TOOLS, TOOL_NAMES } from '../tool-definitions.js';
4
+ // Import all handlers
5
+ import { handleBrowserInit, handleBrowserClose } from '../handlers/browser-handlers.js';
6
+ import { handleNavigate, handleWait } from '../handlers/navigation-handlers.js';
7
+ import { handleClick, handleType, handleSolveCaptcha, handleRandomScroll } from '../handlers/interaction-handlers.js';
8
+ import { handleGetContent, handleFindSelector } from '../handlers/content-handlers.js';
9
+ import { handleSaveContentAsMarkdown } from '../handlers/file-handlers.js';
10
+ export class LspTransport {
11
+ connection;
12
+ documents;
13
+ hasWorkspaceFolderCapability = false;
14
+ constructor() {
15
+ // Create a connection for the server using Node's IPC as a transport
16
+ this.connection = createConnection(ProposedFeatures.all);
17
+ // Create a simple text document manager
18
+ this.documents = new TextDocuments(TextDocument);
19
+ this.setupHandlers();
20
+ // Make the text document manager listen on the connection
21
+ this.documents.listen(this.connection);
22
+ }
23
+ setupHandlers() {
24
+ // Initialize
25
+ this.connection.onInitialize((params) => {
26
+ const capabilities = params.capabilities;
27
+ this.hasWorkspaceFolderCapability = !!(capabilities.workspace && !!capabilities.workspace.workspaceFolders);
28
+ const result = {
29
+ capabilities: {
30
+ textDocumentSync: TextDocumentSyncKind.Incremental,
31
+ completionProvider: {
32
+ resolveProvider: true,
33
+ triggerCharacters: ['.', ':'],
34
+ },
35
+ executeCommandProvider: {
36
+ commands: TOOLS.map(tool => `browser.${tool.name}`),
37
+ },
38
+ hoverProvider: true,
39
+ definitionProvider: true,
40
+ },
41
+ };
42
+ if (this.hasWorkspaceFolderCapability) {
43
+ result.capabilities.workspace = {
44
+ workspaceFolders: {
45
+ supported: true,
46
+ },
47
+ };
48
+ }
49
+ return result;
50
+ });
51
+ this.connection.onInitialized(() => {
52
+ console.error('✅ [LSP] Server initialized');
53
+ if (this.hasWorkspaceFolderCapability) {
54
+ this.connection.workspace.onDidChangeWorkspaceFolders((_event) => {
55
+ console.error('[LSP] Workspace folder change event received');
56
+ });
57
+ }
58
+ });
59
+ // Completion
60
+ this.connection.onCompletion((_textDocumentPosition) => {
61
+ return TOOLS.map(tool => ({
62
+ label: tool.name,
63
+ kind: CompletionItemKind.Function,
64
+ data: tool.name,
65
+ detail: tool.description,
66
+ documentation: JSON.stringify(tool.inputSchema, null, 2),
67
+ }));
68
+ });
69
+ this.connection.onCompletionResolve((item) => {
70
+ return item;
71
+ });
72
+ // Execute command (actual browser automation)
73
+ this.connection.onExecuteCommand(async (params) => {
74
+ const commandName = params.command.replace('browser.', '');
75
+ const args = params.arguments?.[0] || {};
76
+ console.error(`[LSP] Executing command: ${commandName}`);
77
+ try {
78
+ const result = await this.executeTool(commandName, args);
79
+ this.connection.window.showInformationMessage(`✅ Command ${commandName} executed successfully`);
80
+ return result;
81
+ }
82
+ catch (error) {
83
+ const errorMessage = error instanceof Error ? error.message : String(error);
84
+ this.connection.window.showErrorMessage(`❌ Command ${commandName} failed: ${errorMessage}`);
85
+ throw error;
86
+ }
87
+ });
88
+ // Hover information
89
+ this.connection.onHover((params) => {
90
+ const document = this.documents.get(params.textDocument.uri);
91
+ if (!document)
92
+ return null;
93
+ const position = params.position;
94
+ const word = this.getWordAtPosition(document, position);
95
+ const tool = TOOLS.find(t => t.name === word);
96
+ if (tool) {
97
+ return {
98
+ contents: {
99
+ kind: 'markdown',
100
+ value: [
101
+ `**${tool.name}**`,
102
+ '',
103
+ tool.description,
104
+ '',
105
+ '**Parameters:**',
106
+ '```json',
107
+ JSON.stringify(tool.inputSchema, null, 2),
108
+ '```',
109
+ ].join('\n'),
110
+ },
111
+ };
112
+ }
113
+ return null;
114
+ });
115
+ }
116
+ getWordAtPosition(document, position) {
117
+ const text = document.getText();
118
+ const offset = document.offsetAt(position);
119
+ let start = offset;
120
+ let end = offset;
121
+ // Find word boundaries
122
+ while (start > 0 && /\w/.test(text[start - 1]))
123
+ start--;
124
+ while (end < text.length && /\w/.test(text[end]))
125
+ end++;
126
+ return text.substring(start, end);
127
+ }
128
+ async executeTool(toolName, args) {
129
+ switch (toolName) {
130
+ case TOOL_NAMES.BROWSER_INIT:
131
+ return await handleBrowserInit(args || {});
132
+ case TOOL_NAMES.NAVIGATE:
133
+ return await handleNavigate(args);
134
+ case TOOL_NAMES.GET_CONTENT:
135
+ return await handleGetContent(args || {});
136
+ case TOOL_NAMES.CLICK:
137
+ return await handleClick(args);
138
+ case TOOL_NAMES.TYPE:
139
+ return await handleType(args);
140
+ case TOOL_NAMES.WAIT:
141
+ return await handleWait(args);
142
+ case TOOL_NAMES.BROWSER_CLOSE:
143
+ return await handleBrowserClose();
144
+ case TOOL_NAMES.SOLVE_CAPTCHA:
145
+ return await handleSolveCaptcha(args);
146
+ case TOOL_NAMES.RANDOM_SCROLL:
147
+ return await handleRandomScroll();
148
+ case TOOL_NAMES.FIND_SELECTOR:
149
+ return await handleFindSelector(args);
150
+ case TOOL_NAMES.SAVE_CONTENT_AS_MARKDOWN:
151
+ return await handleSaveContentAsMarkdown(args);
152
+ default:
153
+ throw new Error(`Unknown tool: ${toolName}`);
154
+ }
155
+ }
156
+ async start() {
157
+ console.error('🔍 [LSP] Starting Language Server...');
158
+ this.connection.listen();
159
+ console.error('✅ [LSP] Language Server started and listening');
160
+ }
161
+ async stop() {
162
+ console.error('[LSP] Stopping server...');
163
+ this.connection.dispose();
164
+ }
165
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brave-real-browser-mcp-server",
3
- "version": "2.11.6",
3
+ "version": "2.11.7",
4
4
  "description": "MCP server for brave-real-browser",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -18,7 +18,12 @@
18
18
  "build": "tsc",
19
19
  "rebuild": "npm run clean && npm run build",
20
20
  "start": "node dist/index.js",
21
+ "start:http": "node dist/index.js --mode http",
22
+ "start:lsp": "node dist/index.js --mode lsp",
23
+ "start:all": "node dist/index.js --mode all",
21
24
  "dev": "tsx src/index.ts",
25
+ "dev:http": "tsx src/index.ts --mode http",
26
+ "dev:lsp": "tsx src/index.ts --mode lsp",
22
27
  "test": "vitest",
23
28
  "test:watch": "vitest --watch",
24
29
  "test:ui": "vitest --ui",
@@ -37,11 +42,12 @@
37
42
  "axios": "^1.6.5",
38
43
  "brave-real-browser": "^1.5.102",
39
44
  "brave-real-launcher": "^1.2.17",
40
- "brave-real-puppeteer-core": "^24.24.0",
45
+ "brave-real-puppeteer-core": "^24.25.0",
41
46
  "cheerio": "^1.0.0-rc.12",
42
47
  "chrono-node": "^2.7.0",
43
48
  "compromise": "^14.13.0",
44
49
  "dotenv": "^17.2.3",
50
+ "express": "^4.21.2",
45
51
  "franc": "^6.2.0",
46
52
  "libphonenumber-js": "^1.10.51",
47
53
  "natural": "^6.12.0",
@@ -51,11 +57,16 @@
51
57
  "sentiment": "^5.0.2",
52
58
  "tesseract.js": "^5.0.5",
53
59
  "turndown": "^7.2.1",
60
+ "vscode-languageserver": "^9.0.1",
61
+ "vscode-languageserver-textdocument": "^1.0.12",
62
+ "ws": "^8.18.3",
54
63
  "xml2js": "^0.6.2"
55
64
  },
56
65
  "devDependencies": {
57
66
  "@types/cheerio": "^0.22.35",
67
+ "@types/express": "^4.17.23",
58
68
  "@types/node": "latest",
69
+ "@types/ws": "^8.18.1",
59
70
  "@types/xml2js": "^0.4.14",
60
71
  "@vitest/coverage-v8": "^3.2.4",
61
72
  "@vitest/ui": "^3.2.4",
@@ -100,6 +111,6 @@
100
111
  "puppeteer": "npm:brave-real-puppeteer-core@latest",
101
112
  "puppeteer-core": "npm:brave-real-puppeteer-core@latest",
102
113
  "tar-fs": "^3.0.0",
103
- "ws": "^8.18.0"
114
+ "ws": "^8.18.3"
104
115
  }
105
116
  }