brave-real-browser-mcp-server 2.27.1 → 2.27.3
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.
|
@@ -349,6 +349,15 @@ connection.onInitialize(() => {
|
|
|
349
349
|
codeActionProvider: {
|
|
350
350
|
codeActionKinds: [CodeActionKind.QuickFix],
|
|
351
351
|
},
|
|
352
|
+
// Advanced LSP Features
|
|
353
|
+
definitionProvider: true,
|
|
354
|
+
referencesProvider: true,
|
|
355
|
+
workspaceSymbolProvider: true,
|
|
356
|
+
documentSymbolProvider: true,
|
|
357
|
+
renameProvider: {
|
|
358
|
+
prepareProvider: true,
|
|
359
|
+
},
|
|
360
|
+
documentFormattingProvider: true,
|
|
352
361
|
},
|
|
353
362
|
};
|
|
354
363
|
});
|
|
@@ -572,6 +581,236 @@ connection.onCodeAction((params) => {
|
|
|
572
581
|
// ============================================================
|
|
573
582
|
// HELPERS
|
|
574
583
|
// ============================================================
|
|
584
|
+
// Store document contents and tool locations
|
|
585
|
+
const documentToolLocations = new Map();
|
|
586
|
+
function findToolLocations(document) {
|
|
587
|
+
const text = document.getText();
|
|
588
|
+
const locations = [];
|
|
589
|
+
const toolCallPattern = /(\w+)\s*\(/g;
|
|
590
|
+
let match;
|
|
591
|
+
while ((match = toolCallPattern.exec(text)) !== null) {
|
|
592
|
+
const toolName = match[1];
|
|
593
|
+
if (TOOL_DEFINITIONS[toolName]) {
|
|
594
|
+
const startPos = document.positionAt(match.index);
|
|
595
|
+
const endPos = document.positionAt(match.index + toolName.length);
|
|
596
|
+
locations.push({
|
|
597
|
+
name: toolName,
|
|
598
|
+
range: { start: startPos, end: endPos },
|
|
599
|
+
uri: document.uri,
|
|
600
|
+
});
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
documentToolLocations.set(document.uri, locations);
|
|
604
|
+
return locations;
|
|
605
|
+
}
|
|
606
|
+
// ============================================================
|
|
607
|
+
// GO TO DEFINITION
|
|
608
|
+
// ============================================================
|
|
609
|
+
connection.onDefinition((params) => {
|
|
610
|
+
const document = documents.get(params.textDocument.uri);
|
|
611
|
+
if (!document)
|
|
612
|
+
return null;
|
|
613
|
+
const text = document.getText();
|
|
614
|
+
const offset = document.offsetAt(params.position);
|
|
615
|
+
// Find word at position
|
|
616
|
+
const wordStart = text.substring(0, offset).search(/\w+$/);
|
|
617
|
+
if (wordStart < 0)
|
|
618
|
+
return null;
|
|
619
|
+
const wordEnd = offset + text.substring(offset).search(/[^\w]|$/);
|
|
620
|
+
const word = text.substring(wordStart, wordEnd);
|
|
621
|
+
const def = TOOL_DEFINITIONS[word];
|
|
622
|
+
if (def) {
|
|
623
|
+
// Return the first occurrence of this tool in the document
|
|
624
|
+
const pattern = new RegExp(`\\b${word}\\s*\\(`);
|
|
625
|
+
const match = text.match(pattern);
|
|
626
|
+
if (match && match.index !== undefined) {
|
|
627
|
+
const startPos = document.positionAt(match.index);
|
|
628
|
+
const endPos = document.positionAt(match.index + word.length);
|
|
629
|
+
return {
|
|
630
|
+
uri: params.textDocument.uri,
|
|
631
|
+
range: { start: startPos, end: endPos },
|
|
632
|
+
};
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
return null;
|
|
636
|
+
});
|
|
637
|
+
// ============================================================
|
|
638
|
+
// FIND ALL REFERENCES
|
|
639
|
+
// ============================================================
|
|
640
|
+
connection.onReferences((params) => {
|
|
641
|
+
const document = documents.get(params.textDocument.uri);
|
|
642
|
+
if (!document)
|
|
643
|
+
return [];
|
|
644
|
+
const text = document.getText();
|
|
645
|
+
const offset = document.offsetAt(params.position);
|
|
646
|
+
// Find word at position
|
|
647
|
+
const wordStart = text.substring(0, offset).search(/\w+$/);
|
|
648
|
+
if (wordStart < 0)
|
|
649
|
+
return [];
|
|
650
|
+
const wordEnd = offset + text.substring(offset).search(/[^\w]|$/);
|
|
651
|
+
const word = text.substring(wordStart, wordEnd);
|
|
652
|
+
const references = [];
|
|
653
|
+
// Search in all open documents
|
|
654
|
+
documents.all().forEach((doc) => {
|
|
655
|
+
const docText = doc.getText();
|
|
656
|
+
const pattern = new RegExp(`\\b${word}\\b`, 'g');
|
|
657
|
+
let match;
|
|
658
|
+
while ((match = pattern.exec(docText)) !== null) {
|
|
659
|
+
const startPos = doc.positionAt(match.index);
|
|
660
|
+
const endPos = doc.positionAt(match.index + word.length);
|
|
661
|
+
references.push({
|
|
662
|
+
uri: doc.uri,
|
|
663
|
+
range: { start: startPos, end: endPos },
|
|
664
|
+
});
|
|
665
|
+
}
|
|
666
|
+
});
|
|
667
|
+
return references;
|
|
668
|
+
});
|
|
669
|
+
// ============================================================
|
|
670
|
+
// DOCUMENT SYMBOLS
|
|
671
|
+
// ============================================================
|
|
672
|
+
connection.onDocumentSymbol((params) => {
|
|
673
|
+
const document = documents.get(params.textDocument.uri);
|
|
674
|
+
if (!document)
|
|
675
|
+
return [];
|
|
676
|
+
const text = document.getText();
|
|
677
|
+
const symbols = [];
|
|
678
|
+
// Find all tool calls as symbols
|
|
679
|
+
const toolCallPattern = /(\w+)\s*\(\s*\{[^}]*\}\s*\)/g;
|
|
680
|
+
let match;
|
|
681
|
+
while ((match = toolCallPattern.exec(text)) !== null) {
|
|
682
|
+
const toolName = match[1];
|
|
683
|
+
if (TOOL_DEFINITIONS[toolName]) {
|
|
684
|
+
const startPos = document.positionAt(match.index);
|
|
685
|
+
const endPos = document.positionAt(match.index + match[0].length);
|
|
686
|
+
symbols.push({
|
|
687
|
+
name: toolName,
|
|
688
|
+
kind: 12, // Function
|
|
689
|
+
range: { start: startPos, end: endPos },
|
|
690
|
+
selectionRange: { start: startPos, end: document.positionAt(match.index + toolName.length) },
|
|
691
|
+
detail: TOOL_DEFINITIONS[toolName].category,
|
|
692
|
+
});
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
return symbols;
|
|
696
|
+
});
|
|
697
|
+
// ============================================================
|
|
698
|
+
// WORKSPACE SYMBOLS
|
|
699
|
+
// ============================================================
|
|
700
|
+
connection.onWorkspaceSymbol((params) => {
|
|
701
|
+
const query = params.query.toLowerCase();
|
|
702
|
+
const symbols = [];
|
|
703
|
+
// Return all matching tool definitions
|
|
704
|
+
for (const [name, def] of Object.entries(TOOL_DEFINITIONS)) {
|
|
705
|
+
if (name.toLowerCase().includes(query) || def.description.toLowerCase().includes(query)) {
|
|
706
|
+
symbols.push({
|
|
707
|
+
name: name,
|
|
708
|
+
kind: 12, // Function
|
|
709
|
+
location: {
|
|
710
|
+
uri: 'brave-mcp://tools/' + name,
|
|
711
|
+
range: { start: { line: 0, character: 0 }, end: { line: 0, character: name.length } },
|
|
712
|
+
},
|
|
713
|
+
containerName: def.category,
|
|
714
|
+
});
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
return symbols;
|
|
718
|
+
});
|
|
719
|
+
// ============================================================
|
|
720
|
+
// RENAME SYMBOL
|
|
721
|
+
// ============================================================
|
|
722
|
+
connection.onPrepareRename((params) => {
|
|
723
|
+
const document = documents.get(params.textDocument.uri);
|
|
724
|
+
if (!document)
|
|
725
|
+
return null;
|
|
726
|
+
const text = document.getText();
|
|
727
|
+
const offset = document.offsetAt(params.position);
|
|
728
|
+
// Find word at position
|
|
729
|
+
const wordStart = text.substring(0, offset).search(/\w+$/);
|
|
730
|
+
if (wordStart < 0)
|
|
731
|
+
return null;
|
|
732
|
+
const wordEnd = offset + text.substring(offset).search(/[^\w]|$/);
|
|
733
|
+
const word = text.substring(wordStart, wordEnd);
|
|
734
|
+
// Don't allow renaming built-in tools
|
|
735
|
+
if (TOOL_DEFINITIONS[word]) {
|
|
736
|
+
return { message: 'Cannot rename built-in MCP tool' };
|
|
737
|
+
}
|
|
738
|
+
const startPos = document.positionAt(wordStart);
|
|
739
|
+
const endPos = document.positionAt(wordEnd);
|
|
740
|
+
return { range: { start: startPos, end: endPos }, placeholder: word };
|
|
741
|
+
});
|
|
742
|
+
connection.onRenameRequest((params) => {
|
|
743
|
+
const document = documents.get(params.textDocument.uri);
|
|
744
|
+
if (!document)
|
|
745
|
+
return null;
|
|
746
|
+
const text = document.getText();
|
|
747
|
+
const offset = document.offsetAt(params.position);
|
|
748
|
+
// Find word at position
|
|
749
|
+
const wordStart = text.substring(0, offset).search(/\w+$/);
|
|
750
|
+
if (wordStart < 0)
|
|
751
|
+
return null;
|
|
752
|
+
const wordEnd = offset + text.substring(offset).search(/[^\w]|$/);
|
|
753
|
+
const oldName = text.substring(wordStart, wordEnd);
|
|
754
|
+
const newName = params.newName;
|
|
755
|
+
// Don't allow renaming built-in tools
|
|
756
|
+
if (TOOL_DEFINITIONS[oldName]) {
|
|
757
|
+
return null;
|
|
758
|
+
}
|
|
759
|
+
const changes = {};
|
|
760
|
+
// Replace all occurrences in all open documents
|
|
761
|
+
documents.all().forEach((doc) => {
|
|
762
|
+
const docText = doc.getText();
|
|
763
|
+
const pattern = new RegExp(`\\b${oldName}\\b`, 'g');
|
|
764
|
+
const edits = [];
|
|
765
|
+
let match;
|
|
766
|
+
while ((match = pattern.exec(docText)) !== null) {
|
|
767
|
+
const startPos = doc.positionAt(match.index);
|
|
768
|
+
const endPos = doc.positionAt(match.index + oldName.length);
|
|
769
|
+
edits.push({
|
|
770
|
+
range: { start: startPos, end: endPos },
|
|
771
|
+
newText: newName,
|
|
772
|
+
});
|
|
773
|
+
}
|
|
774
|
+
if (edits.length > 0) {
|
|
775
|
+
changes[doc.uri] = edits;
|
|
776
|
+
}
|
|
777
|
+
});
|
|
778
|
+
return { changes };
|
|
779
|
+
});
|
|
780
|
+
// ============================================================
|
|
781
|
+
// FORMAT DOCUMENT
|
|
782
|
+
// ============================================================
|
|
783
|
+
connection.onDocumentFormatting((params) => {
|
|
784
|
+
const document = documents.get(params.textDocument.uri);
|
|
785
|
+
if (!document)
|
|
786
|
+
return [];
|
|
787
|
+
const text = document.getText();
|
|
788
|
+
const edits = [];
|
|
789
|
+
// Format MCP tool calls consistently
|
|
790
|
+
let formatted = text
|
|
791
|
+
// Normalize tool call opening: tool({
|
|
792
|
+
.replace(/(\w+)\s*\(\s*\{\s*/g, '$1({ ')
|
|
793
|
+
// Normalize tool call closing: })
|
|
794
|
+
.replace(/\s*\}\s*\)/g, ' })')
|
|
795
|
+
// Fix parameter spacing: key: value
|
|
796
|
+
.replace(/(\w+)\s*:\s*/g, '$1: ')
|
|
797
|
+
// Remove trailing commas before }
|
|
798
|
+
.replace(/,\s*\}/g, ' }')
|
|
799
|
+
// Normalize await spacing
|
|
800
|
+
.replace(/await\s+/g, 'await ');
|
|
801
|
+
if (formatted !== text) {
|
|
802
|
+
edits.push({
|
|
803
|
+
range: {
|
|
804
|
+
start: { line: 0, character: 0 },
|
|
805
|
+
end: document.positionAt(text.length),
|
|
806
|
+
},
|
|
807
|
+
newText: formatted,
|
|
808
|
+
});
|
|
809
|
+
}
|
|
810
|
+
return edits;
|
|
811
|
+
});
|
|
812
|
+
// ============================================================
|
|
813
|
+
// HELPERS
|
|
575
814
|
function formatToolDoc(def) {
|
|
576
815
|
const parts = [];
|
|
577
816
|
parts.push(`## ${def.name}`);
|
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.3",
|
|
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.3",
|
|
54
54
|
"puppeteer-core": "^24.35.0",
|
|
55
55
|
"turndown": "latest",
|
|
56
56
|
"vscode-languageserver": "^9.0.1",
|