brave-real-browser-mcp-server 2.17.11 → 2.17.13
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 +51 -64
- package/dist/handlers/deep-analysis-handler.js +119 -0
- package/dist/handlers/unified-captcha-handler.js +137 -0
- package/dist/handlers/unified-search-handler.js +137 -0
- package/dist/index.js +16 -31
- package/dist/tool-definitions.js +58 -97
- package/dist/workflows/forensic-media-extractor.js +2 -2
- package/package.json +1 -1
- package/dist/handlers/captcha-handlers.js +0 -257
- package/dist/handlers/data-quality-handlers.js +0 -82
- package/dist/handlers/search-filter-handlers.js +0 -264
|
@@ -1,264 +0,0 @@
|
|
|
1
|
-
import { getPageInstance } from '../browser-manager.js';
|
|
2
|
-
/**
|
|
3
|
-
* Keyword Search - Advanced keyword search in page content
|
|
4
|
-
*/
|
|
5
|
-
export async function handleKeywordSearch(args) {
|
|
6
|
-
const { url, keywords, caseSensitive = false, wholeWord = false, context = 50 } = args;
|
|
7
|
-
try {
|
|
8
|
-
const page = getPageInstance();
|
|
9
|
-
if (!page) {
|
|
10
|
-
throw new Error('Browser not initialized. Call browser_init first.');
|
|
11
|
-
}
|
|
12
|
-
if (url && page.url() !== url) {
|
|
13
|
-
await page.goto(url, { waitUntil: 'networkidle2', timeout: 30000 });
|
|
14
|
-
}
|
|
15
|
-
const results = await page.evaluate((kws, caseSens, whole, ctx) => {
|
|
16
|
-
const allMatches = [];
|
|
17
|
-
const keywordList = Array.isArray(kws) ? kws : [kws];
|
|
18
|
-
keywordList.forEach(keyword => {
|
|
19
|
-
const flags = caseSens ? 'g' : 'gi';
|
|
20
|
-
const pattern = whole ? `\\b${keyword}\\b` : keyword;
|
|
21
|
-
//const regex = new RegExp(pattern, flags);
|
|
22
|
-
const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT, null);
|
|
23
|
-
let node;
|
|
24
|
-
while (node = walker.nextNode()) {
|
|
25
|
-
const text = node.textContent || '';
|
|
26
|
-
let match;
|
|
27
|
-
const nodeRegex = new RegExp(pattern, flags);
|
|
28
|
-
while ((match = nodeRegex.exec(text)) !== null) {
|
|
29
|
-
const start = Math.max(0, match.index - ctx);
|
|
30
|
-
const end = Math.min(text.length, match.index + match[0].length + ctx);
|
|
31
|
-
const contextText = text.substring(start, end);
|
|
32
|
-
// Get element info
|
|
33
|
-
const element = node.parentElement;
|
|
34
|
-
const tagName = element?.tagName.toLowerCase() || 'text';
|
|
35
|
-
const className = element?.className || '';
|
|
36
|
-
const id = element?.id || '';
|
|
37
|
-
allMatches.push({
|
|
38
|
-
keyword,
|
|
39
|
-
match: match[0],
|
|
40
|
-
position: match.index,
|
|
41
|
-
context: contextText,
|
|
42
|
-
element: {
|
|
43
|
-
tag: tagName,
|
|
44
|
-
class: className,
|
|
45
|
-
id: id
|
|
46
|
-
}
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
});
|
|
51
|
-
// Group by keyword
|
|
52
|
-
const grouped = {};
|
|
53
|
-
allMatches.forEach(m => {
|
|
54
|
-
if (!grouped[m.keyword])
|
|
55
|
-
grouped[m.keyword] = [];
|
|
56
|
-
grouped[m.keyword].push(m);
|
|
57
|
-
});
|
|
58
|
-
return {
|
|
59
|
-
totalMatches: allMatches.length,
|
|
60
|
-
matchesByKeyword: grouped,
|
|
61
|
-
allMatches: allMatches.slice(0, 100) // Limit to first 100
|
|
62
|
-
};
|
|
63
|
-
}, Array.isArray(keywords) ? keywords : [keywords], caseSensitive, wholeWord, context);
|
|
64
|
-
const resultText = `✅ Keyword Search Results\n\nTotal Matches: ${results.totalMatches}\n\nKeywords searched: ${Array.isArray(keywords) ? keywords.join(', ') : keywords}\n\nMatches by keyword:\n${JSON.stringify(results.matchesByKeyword, null, 2)}\n\nFirst 100 matches:\n${JSON.stringify(results.allMatches, null, 2)}`;
|
|
65
|
-
return {
|
|
66
|
-
content: [
|
|
67
|
-
{
|
|
68
|
-
type: 'text',
|
|
69
|
-
text: resultText,
|
|
70
|
-
},
|
|
71
|
-
],
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
catch (error) {
|
|
75
|
-
return {
|
|
76
|
-
content: [{ type: 'text', text: `❌ Keyword search failed: ${error.message}` }],
|
|
77
|
-
isError: true,
|
|
78
|
-
};
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
/**
|
|
82
|
-
* Regex Pattern Matcher - Search using regular expressions
|
|
83
|
-
*/
|
|
84
|
-
export async function handleRegexPatternMatcher(args) {
|
|
85
|
-
const { url, pattern, flags = 'g', selector } = args;
|
|
86
|
-
try {
|
|
87
|
-
const page = getPageInstance();
|
|
88
|
-
if (!page) {
|
|
89
|
-
throw new Error('Browser not initialized. Call browser_init first.');
|
|
90
|
-
}
|
|
91
|
-
if (url && page.url() !== url) {
|
|
92
|
-
await page.goto(url, { waitUntil: 'networkidle2', timeout: 30000 });
|
|
93
|
-
}
|
|
94
|
-
const results = await page.evaluate((pat, flgs, sel) => {
|
|
95
|
-
let content;
|
|
96
|
-
if (sel) {
|
|
97
|
-
const element = document.querySelector(sel);
|
|
98
|
-
content = element ? element.textContent || '' : '';
|
|
99
|
-
}
|
|
100
|
-
else {
|
|
101
|
-
content = document.body.innerText;
|
|
102
|
-
}
|
|
103
|
-
const regex = new RegExp(pat, flgs);
|
|
104
|
-
const matches = [];
|
|
105
|
-
let match;
|
|
106
|
-
// Safety check for infinite loop
|
|
107
|
-
let count = 0;
|
|
108
|
-
while ((match = regex.exec(content)) !== null && count < 1000) {
|
|
109
|
-
count++;
|
|
110
|
-
matches.push({
|
|
111
|
-
match: match[0],
|
|
112
|
-
index: match.index,
|
|
113
|
-
groups: match.slice(1),
|
|
114
|
-
context: content.substring(Math.max(0, match.index - 50), Math.min(content.length, match.index + match[0].length + 50))
|
|
115
|
-
});
|
|
116
|
-
if (match.index === regex.lastIndex) {
|
|
117
|
-
regex.lastIndex++;
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
return {
|
|
121
|
-
totalMatches: matches.length,
|
|
122
|
-
matches: matches.slice(0, 100),
|
|
123
|
-
pattern: pat,
|
|
124
|
-
flags: flgs
|
|
125
|
-
};
|
|
126
|
-
}, pattern, flags, selector || '');
|
|
127
|
-
const resultText = `✅ Regex Pattern Matcher Results\n\nPattern: ${results.pattern}\nFlags: ${results.flags}\nTotal Matches: ${results.totalMatches}\n\nMatches (first 100):\n${JSON.stringify(results.matches, null, 2)}`;
|
|
128
|
-
return {
|
|
129
|
-
content: [{ type: 'text', text: resultText }],
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
|
-
catch (error) {
|
|
133
|
-
return { content: [{ type: 'text', text: `❌ Regex pattern matcher failed: ${error.message}` }], isError: true };
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
/**
|
|
137
|
-
* XPath Support - Query elements using XPath
|
|
138
|
-
*/
|
|
139
|
-
export async function handleXPathSupport(args) {
|
|
140
|
-
const { url, xpath, returnType = 'elements' } = args;
|
|
141
|
-
try {
|
|
142
|
-
const page = getPageInstance();
|
|
143
|
-
if (!page) {
|
|
144
|
-
throw new Error('Browser not initialized. Call browser_init first.');
|
|
145
|
-
}
|
|
146
|
-
if (url && page.url() !== url) {
|
|
147
|
-
await page.goto(url, { waitUntil: 'networkidle2', timeout: 30000 });
|
|
148
|
-
}
|
|
149
|
-
const results = await page.evaluate((xp, type) => {
|
|
150
|
-
const xpathResult = document.evaluate(xp, document, null, XPathResult.ANY_TYPE, null);
|
|
151
|
-
const elements = [];
|
|
152
|
-
let node = xpathResult.iterateNext();
|
|
153
|
-
while (node) {
|
|
154
|
-
if (node.nodeType === Node.ELEMENT_NODE) {
|
|
155
|
-
const element = node;
|
|
156
|
-
elements.push({
|
|
157
|
-
tagName: element.tagName.toLowerCase(),
|
|
158
|
-
id: element.id,
|
|
159
|
-
className: element.className,
|
|
160
|
-
text: element.textContent?.substring(0, 200),
|
|
161
|
-
attributes: Array.from(element.attributes).reduce((acc, attr) => {
|
|
162
|
-
acc[attr.name] = attr.value;
|
|
163
|
-
return acc;
|
|
164
|
-
}, {}),
|
|
165
|
-
innerHTML: type === 'html' ? element.innerHTML.substring(0, 500) : undefined
|
|
166
|
-
});
|
|
167
|
-
}
|
|
168
|
-
else if (node.nodeType === Node.TEXT_NODE) {
|
|
169
|
-
elements.push({
|
|
170
|
-
type: 'text',
|
|
171
|
-
content: node.textContent?.trim()
|
|
172
|
-
});
|
|
173
|
-
}
|
|
174
|
-
else if (node.nodeType === Node.ATTRIBUTE_NODE) {
|
|
175
|
-
const attr = node;
|
|
176
|
-
elements.push({
|
|
177
|
-
type: 'attribute',
|
|
178
|
-
name: attr.name,
|
|
179
|
-
value: attr.value
|
|
180
|
-
});
|
|
181
|
-
}
|
|
182
|
-
node = xpathResult.iterateNext();
|
|
183
|
-
}
|
|
184
|
-
return {
|
|
185
|
-
count: elements.length,
|
|
186
|
-
elements
|
|
187
|
-
};
|
|
188
|
-
}, xpath, returnType);
|
|
189
|
-
const resultText = `✅ XPath Query Results\n\nXPath: ${xpath}\nElements Found: ${results.count}\n\nElements:\n${JSON.stringify(results.elements, null, 2)}`;
|
|
190
|
-
return {
|
|
191
|
-
content: [{ type: 'text', text: resultText }],
|
|
192
|
-
};
|
|
193
|
-
}
|
|
194
|
-
catch (error) {
|
|
195
|
-
return { content: [{ type: 'text', text: `❌ XPath query failed: ${error.message}` }], isError: true };
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
/**
|
|
199
|
-
* Advanced CSS Selectors - Support for complex CSS selectors
|
|
200
|
-
*/
|
|
201
|
-
export async function handleAdvancedCSSSelectors(args) {
|
|
202
|
-
const { url, selector, operation = 'query', returnType = 'elements' } = args;
|
|
203
|
-
try {
|
|
204
|
-
const page = getPageInstance();
|
|
205
|
-
if (!page) {
|
|
206
|
-
throw new Error('Browser not initialized. Call browser_init first.');
|
|
207
|
-
}
|
|
208
|
-
if (url && page.url() !== url) {
|
|
209
|
-
await page.goto(url, { waitUntil: 'networkidle2', timeout: 30000 });
|
|
210
|
-
}
|
|
211
|
-
const results = await page.evaluate((sel, op, type) => {
|
|
212
|
-
let elements;
|
|
213
|
-
switch (op) {
|
|
214
|
-
case 'query':
|
|
215
|
-
elements = Array.from(document.querySelectorAll(sel));
|
|
216
|
-
break;
|
|
217
|
-
case 'closest':
|
|
218
|
-
const firstEl = document.querySelector(sel);
|
|
219
|
-
elements = firstEl ? [firstEl.closest(sel)].filter(Boolean) : [];
|
|
220
|
-
break;
|
|
221
|
-
case 'matches':
|
|
222
|
-
elements = Array.from(document.querySelectorAll('*')).filter(el => el.matches(sel));
|
|
223
|
-
break;
|
|
224
|
-
default:
|
|
225
|
-
elements = Array.from(document.querySelectorAll(sel));
|
|
226
|
-
}
|
|
227
|
-
const results = elements.map(element => {
|
|
228
|
-
const computed = window.getComputedStyle(element);
|
|
229
|
-
return {
|
|
230
|
-
tagName: element.tagName.toLowerCase(),
|
|
231
|
-
id: element.id,
|
|
232
|
-
className: element.className,
|
|
233
|
-
text: element.textContent?.substring(0, 200),
|
|
234
|
-
attributes: Array.from(element.attributes).reduce((acc, attr) => {
|
|
235
|
-
acc[attr.name] = attr.value;
|
|
236
|
-
return acc;
|
|
237
|
-
}, {}),
|
|
238
|
-
computedStyles: type === 'styles' ? {
|
|
239
|
-
display: computed.display,
|
|
240
|
-
visibility: computed.visibility,
|
|
241
|
-
position: computed.position,
|
|
242
|
-
width: computed.width,
|
|
243
|
-
height: computed.height,
|
|
244
|
-
color: computed.color,
|
|
245
|
-
backgroundColor: computed.backgroundColor
|
|
246
|
-
} : undefined,
|
|
247
|
-
innerHTML: type === 'html' ? element.innerHTML.substring(0, 500) : undefined,
|
|
248
|
-
boundingRect: element.getBoundingClientRect()
|
|
249
|
-
};
|
|
250
|
-
});
|
|
251
|
-
return {
|
|
252
|
-
count: results.length,
|
|
253
|
-
elements: results
|
|
254
|
-
};
|
|
255
|
-
}, selector, operation, returnType);
|
|
256
|
-
const resultText = `✅ Advanced CSS Selector Results\n\nSelector: ${selector}\nOperation: ${operation}\nElements Found: ${results.count}\n\nElements (first 10):\n${JSON.stringify(results.elements.slice(0, 10), null, 2)}`;
|
|
257
|
-
return {
|
|
258
|
-
content: [{ type: 'text', text: resultText }],
|
|
259
|
-
};
|
|
260
|
-
}
|
|
261
|
-
catch (error) {
|
|
262
|
-
return { content: [{ type: 'text', text: `❌ CSS selector query failed: ${error.message}` }], isError: true };
|
|
263
|
-
}
|
|
264
|
-
}
|