brave-real-browser-mcp-server 2.9.9 → 2.9.10

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.
@@ -49,7 +49,7 @@ export async function handleBrowserClose() {
49
49
  });
50
50
  }
51
51
  // Workflow validation wrapper
52
- async function withWorkflowValidation(toolName, args, operation) {
52
+ export async function withWorkflowValidation(toolName, args, operation) {
53
53
  // Validate workflow state before execution
54
54
  const validation = validateWorkflow(toolName, args);
55
55
  // Defensive check: if validation is undefined or null, allow execution (test environment)
@@ -0,0 +1,464 @@
1
+ // Specialized Tools Handlers for Video Extraction, Links Finding, AJAX, and User Agent Tools
2
+ import { getBrowser, getPage } from '../browser-manager.js';
3
+ import { withWorkflowValidation } from './browser-handlers.js';
4
+ import { withErrorHandling } from '../system-utils.js';
5
+ // Links Finders Handler
6
+ export async function handleLinksFinders(args) {
7
+ return withWorkflowValidation('links_finders', args, async () => {
8
+ return withErrorHandling(async () => {
9
+ const browser = getBrowser();
10
+ const page = getPage();
11
+ if (!browser || !page) {
12
+ throw new Error('Browser not initialized. Call browser_init first.');
13
+ }
14
+ const { includeExternal = true, includeInternal = true, includeMediaLinks = true, includeEmailPhone = false, filterDomains = [] } = args;
15
+ const links = await page.evaluate((options) => {
16
+ const { includeExternal, includeInternal, includeMediaLinks, includeEmailPhone, filterDomains } = options;
17
+ const results = [];
18
+ const currentDomain = window.location.hostname;
19
+ // Find all anchor links
20
+ const anchors = document.querySelectorAll('a[href]');
21
+ anchors.forEach((anchor) => {
22
+ const href = anchor.href;
23
+ const isExternal = !href.includes(currentDomain);
24
+ if ((isExternal && includeExternal) || (!isExternal && includeInternal)) {
25
+ if (filterDomains.length === 0 || filterDomains.some((domain) => href.includes(domain))) {
26
+ results.push({
27
+ type: 'anchor',
28
+ url: href,
29
+ text: anchor.textContent?.trim() || '',
30
+ isExternal,
31
+ title: anchor.title || ''
32
+ });
33
+ }
34
+ }
35
+ });
36
+ // Find media links if requested
37
+ if (includeMediaLinks) {
38
+ const mediaElements = document.querySelectorAll('img[src], video[src], audio[src], source[src]');
39
+ mediaElements.forEach((element) => {
40
+ results.push({
41
+ type: 'media',
42
+ url: element.src,
43
+ mediaType: element.tagName.toLowerCase(),
44
+ alt: element.alt || '',
45
+ title: element.title || ''
46
+ });
47
+ });
48
+ }
49
+ // Find email and phone links if requested
50
+ if (includeEmailPhone) {
51
+ const emailPhoneLinks = document.querySelectorAll('a[href^="mailto:"], a[href^="tel:"]');
52
+ emailPhoneLinks.forEach((link) => {
53
+ results.push({
54
+ type: link.href.startsWith('mailto:') ? 'email' : 'phone',
55
+ url: link.href,
56
+ text: link.textContent?.trim() || ''
57
+ });
58
+ });
59
+ }
60
+ return results;
61
+ }, { includeExternal, includeInternal, includeMediaLinks, includeEmailPhone, filterDomains });
62
+ return {
63
+ success: true,
64
+ links,
65
+ total: links.length,
66
+ message: `Found ${links.length} links on the page`
67
+ };
68
+ }, 'Failed to find links');
69
+ });
70
+ }
71
+ // Video Play Sources Finder Handler
72
+ export async function handleVideoPlaySourcesFinder(args) {
73
+ return withWorkflowValidation('video_play_sources_finder', args, async () => {
74
+ return withErrorHandling(async () => {
75
+ const browser = getBrowser();
76
+ const page = getPage();
77
+ if (!browser || !page) {
78
+ throw new Error('Browser not initialized. Call browser_init first.');
79
+ }
80
+ const { includeHLS = true, includeDASH = true, includeDirectMP4 = true, includeBlob = true, recordingDuration = 15000 } = args;
81
+ // Start network monitoring
82
+ const networkSources = [];
83
+ await page.setRequestInterception(true);
84
+ page.on('request', (request) => {
85
+ request.continue();
86
+ });
87
+ page.on('response', async (response) => {
88
+ try {
89
+ const url = response.url();
90
+ const contentType = response.headers()['content-type'] || '';
91
+ // Check for video sources
92
+ const isVideoSource = ((includeHLS && (url.includes('.m3u8') || contentType.includes('application/x-mpegURL'))) ||
93
+ (includeDASH && (url.includes('.mpd') || contentType.includes('application/dash+xml'))) ||
94
+ (includeDirectMP4 && (url.includes('.mp4') || contentType.includes('video/mp4'))) ||
95
+ (includeBlob && url.startsWith('blob:')));
96
+ if (isVideoSource) {
97
+ networkSources.push({
98
+ url,
99
+ type: url.includes('.m3u8') ? 'HLS' :
100
+ url.includes('.mpd') ? 'DASH' :
101
+ url.includes('.mp4') ? 'MP4' :
102
+ url.startsWith('blob:') ? 'BLOB' : 'UNKNOWN',
103
+ contentType,
104
+ size: response.headers()['content-length'] || 'unknown',
105
+ status: response.status()
106
+ });
107
+ }
108
+ }
109
+ catch (error) {
110
+ // Ignore response processing errors
111
+ }
112
+ });
113
+ // Wait for network activity
114
+ await new Promise(resolve => setTimeout(resolve, recordingDuration));
115
+ await page.setRequestInterception(false);
116
+ // Also search DOM for video sources
117
+ const domSources = await page.evaluate((options) => {
118
+ const { includeHLS, includeDASH, includeDirectMP4, includeBlob } = options;
119
+ const sources = [];
120
+ // Video elements
121
+ const videos = document.querySelectorAll('video');
122
+ videos.forEach((video) => {
123
+ if (video.src)
124
+ sources.push({ url: video.src, type: 'VIDEO_TAG', source: 'dom' });
125
+ const sourceTags = video.querySelectorAll('source');
126
+ sourceTags.forEach((source) => {
127
+ if (source.src)
128
+ sources.push({ url: source.src, type: source.type || 'VIDEO_SOURCE', source: 'dom' });
129
+ });
130
+ });
131
+ // Search for sources in page text/scripts
132
+ const scripts = document.querySelectorAll('script');
133
+ scripts.forEach((script) => {
134
+ const content = script.textContent || '';
135
+ if (includeHLS) {
136
+ const hlsMatches = content.match(/https?:\/\/[^\s"']+\.m3u8[^\s"']*/g);
137
+ if (hlsMatches) {
138
+ hlsMatches.forEach(url => sources.push({ url, type: 'HLS', source: 'script' }));
139
+ }
140
+ }
141
+ if (includeDASH) {
142
+ const dashMatches = content.match(/https?:\/\/[^\s"']+\.mpd[^\s"']*/g);
143
+ if (dashMatches) {
144
+ dashMatches.forEach(url => sources.push({ url, type: 'DASH', source: 'script' }));
145
+ }
146
+ }
147
+ if (includeDirectMP4) {
148
+ const mp4Matches = content.match(/https?:\/\/[^\s"']+\.mp4[^\s"']*/g);
149
+ if (mp4Matches) {
150
+ mp4Matches.forEach(url => sources.push({ url, type: 'MP4', source: 'script' }));
151
+ }
152
+ }
153
+ });
154
+ return sources;
155
+ }, { includeHLS, includeDASH, includeDirectMP4, includeBlob });
156
+ const allSources = [...networkSources, ...domSources];
157
+ const uniqueSources = allSources.filter((source, index, array) => array.findIndex(s => s.url === source.url) === index);
158
+ return {
159
+ success: true,
160
+ sources: uniqueSources,
161
+ networkSources: networkSources.length,
162
+ domSources: domSources.length,
163
+ total: uniqueSources.length,
164
+ message: `Found ${uniqueSources.length} video play sources`
165
+ };
166
+ }, 'Failed to find video play sources');
167
+ });
168
+ }
169
+ // Video Player Hostars Sources Finder Handler
170
+ export async function handleVideoPlayerHostarsSourcesFinder(args) {
171
+ return withWorkflowValidation('video_player_hostars_sources_finder', args, async () => {
172
+ return withErrorHandling(async () => {
173
+ const browser = getBrowser();
174
+ const page = getPage();
175
+ if (!browser || !page) {
176
+ throw new Error('Browser not initialized. Call browser_init first.');
177
+ }
178
+ const { platforms = ['all'], extractEmbedCodes = true, extractAPIKeys = false } = args;
179
+ const hostingSources = await page.evaluate((options) => {
180
+ const { platforms, extractEmbedCodes, extractAPIKeys } = options;
181
+ const sources = [];
182
+ // Define hosting platform patterns
183
+ const hostingPatterns = {
184
+ youtube: [
185
+ /(?:youtube\.com|youtu\.be)/i,
186
+ /\/embed\/([a-zA-Z0-9_-]+)/,
187
+ /watch\?v=([a-zA-Z0-9_-]+)/
188
+ ],
189
+ vimeo: [
190
+ /vimeo\.com/i,
191
+ /\/video\/(\d+)/,
192
+ /player\.vimeo\.com\/video\/(\d+)/
193
+ ],
194
+ dailymotion: [
195
+ /dailymotion\.com/i,
196
+ /\/video\/([a-zA-Z0-9_-]+)/
197
+ ],
198
+ twitch: [
199
+ /twitch\.tv/i,
200
+ /\/videos\/(\d+)/,
201
+ /\/embed\/([a-zA-Z0-9_-]+)/
202
+ ],
203
+ facebook: [
204
+ /facebook\.com/i,
205
+ /\/watch/,
206
+ /\/videos/
207
+ ],
208
+ instagram: [
209
+ /instagram\.com/i,
210
+ /\/p\/([a-zA-Z0-9_-]+)/
211
+ ],
212
+ tiktok: [
213
+ /tiktok\.com/i,
214
+ /@[^\/]+\/video\/(\d+)/
215
+ ]
216
+ };
217
+ // Search in iframes
218
+ const iframes = document.querySelectorAll('iframe');
219
+ iframes.forEach((iframe) => {
220
+ const src = iframe.src;
221
+ if (!src)
222
+ return;
223
+ for (const [platform, patterns] of Object.entries(hostingPatterns)) {
224
+ if (platforms.includes('all') || platforms.includes(platform)) {
225
+ if (patterns[0].test(src)) {
226
+ const videoId = src.match(patterns[1] || patterns[0]);
227
+ sources.push({
228
+ platform,
229
+ url: src,
230
+ videoId: videoId ? videoId[1] : null,
231
+ type: 'iframe',
232
+ embedCode: extractEmbedCodes ? iframe.outerHTML : null
233
+ });
234
+ }
235
+ }
236
+ }
237
+ });
238
+ // Search in links
239
+ const links = document.querySelectorAll('a[href]');
240
+ links.forEach((link) => {
241
+ const href = link.href;
242
+ for (const [platform, patterns] of Object.entries(hostingPatterns)) {
243
+ if (platforms.includes('all') || platforms.includes(platform)) {
244
+ if (patterns[0].test(href)) {
245
+ const videoId = href.match(patterns[1] || patterns[0]);
246
+ sources.push({
247
+ platform,
248
+ url: href,
249
+ videoId: videoId ? videoId[1] : null,
250
+ type: 'link',
251
+ linkText: link.textContent?.trim() || ''
252
+ });
253
+ }
254
+ }
255
+ }
256
+ });
257
+ // Search in script content for API calls and video IDs
258
+ const scripts = document.querySelectorAll('script');
259
+ scripts.forEach((script) => {
260
+ const content = script.textContent || '';
261
+ for (const [platform, patterns] of Object.entries(hostingPatterns)) {
262
+ if (platforms.includes('all') || platforms.includes(platform)) {
263
+ const matches = content.match(patterns[1] || patterns[0]);
264
+ if (matches) {
265
+ matches.forEach(match => {
266
+ sources.push({
267
+ platform,
268
+ url: match,
269
+ type: 'script',
270
+ source: 'javascript'
271
+ });
272
+ });
273
+ }
274
+ }
275
+ }
276
+ });
277
+ return sources;
278
+ }, { platforms, extractEmbedCodes, extractAPIKeys });
279
+ return {
280
+ success: true,
281
+ sources: hostingSources,
282
+ platforms: [...new Set(hostingSources.map((s) => s.platform))],
283
+ total: hostingSources.length,
284
+ message: `Found ${hostingSources.length} hosting platform sources`
285
+ };
286
+ }, 'Failed to find video hosting sources');
287
+ });
288
+ }
289
+ // AJAX Finders Handler
290
+ export async function handleAjaxFinders(args) {
291
+ return withWorkflowValidation('ajax_finders', args, async () => {
292
+ return withErrorHandling(async () => {
293
+ const browser = getBrowser();
294
+ const page = getPage();
295
+ if (!browser || !page) {
296
+ throw new Error('Browser not initialized. Call browser_init first.');
297
+ }
298
+ const { interceptXHR = true, interceptFetch = true, recordResponses = true, filterByContentType = ['application/json', 'text/html', 'application/xml'], monitoringDuration = 10000 } = args;
299
+ const ajaxRequests = [];
300
+ // Intercept network requests
301
+ await page.setRequestInterception(true);
302
+ page.on('request', (request) => {
303
+ const resourceType = request.resourceType();
304
+ if (resourceType === 'xhr' || resourceType === 'fetch') {
305
+ ajaxRequests.push({
306
+ url: request.url(),
307
+ method: request.method(),
308
+ headers: request.headers(),
309
+ postData: request.postData(),
310
+ resourceType,
311
+ timestamp: Date.now(),
312
+ type: 'request'
313
+ });
314
+ }
315
+ request.continue();
316
+ });
317
+ if (recordResponses) {
318
+ page.on('response', async (response) => {
319
+ try {
320
+ const request = response.request();
321
+ const resourceType = request.resourceType();
322
+ if (resourceType === 'xhr' || resourceType === 'fetch') {
323
+ const contentType = response.headers()['content-type'] || '';
324
+ const shouldRecord = filterByContentType.length === 0 ||
325
+ filterByContentType.some((type) => contentType.includes(type));
326
+ if (shouldRecord) {
327
+ let responseBody = '';
328
+ try {
329
+ responseBody = await response.text();
330
+ }
331
+ catch (e) {
332
+ responseBody = '[Unable to read response body]';
333
+ }
334
+ ajaxRequests.push({
335
+ url: response.url(),
336
+ status: response.status(),
337
+ headers: response.headers(),
338
+ contentType,
339
+ body: responseBody.substring(0, 10000), // Limit body size
340
+ timestamp: Date.now(),
341
+ type: 'response'
342
+ });
343
+ }
344
+ }
345
+ }
346
+ catch (error) {
347
+ // Ignore response processing errors
348
+ }
349
+ });
350
+ }
351
+ // Wait for AJAX activity
352
+ await new Promise(resolve => setTimeout(resolve, monitoringDuration));
353
+ await page.setRequestInterception(false);
354
+ // Group requests and responses
355
+ const groupedRequests = ajaxRequests.reduce((acc, item) => {
356
+ const key = item.url;
357
+ if (!acc[key])
358
+ acc[key] = { requests: [], responses: [] };
359
+ if (item.type === 'request') {
360
+ acc[key].requests.push(item);
361
+ }
362
+ else {
363
+ acc[key].responses.push(item);
364
+ }
365
+ return acc;
366
+ }, {});
367
+ return {
368
+ success: true,
369
+ ajaxRequests: groupedRequests,
370
+ totalRequests: ajaxRequests.filter(r => r.type === 'request').length,
371
+ totalResponses: ajaxRequests.filter(r => r.type === 'response').length,
372
+ uniqueEndpoints: Object.keys(groupedRequests).length,
373
+ message: `Monitored AJAX activity for ${monitoringDuration}ms`
374
+ };
375
+ }, 'Failed to monitor AJAX requests');
376
+ });
377
+ }
378
+ // User Agent Finders Handler
379
+ export async function handleUserAgentFinders(args) {
380
+ return withWorkflowValidation('user_agent_finders', args, async () => {
381
+ return withErrorHandling(async () => {
382
+ const browser = getBrowser();
383
+ const page = getPage();
384
+ if (!browser || !page) {
385
+ throw new Error('Browser not initialized. Call browser_init first.');
386
+ }
387
+ const { detectFingerprinting = true, analyzeHeaders = true, findCustomUA = true, trackingDetection = true } = args;
388
+ const userAgentInfo = await page.evaluate((options) => {
389
+ const { detectFingerprinting, analyzeHeaders, findCustomUA, trackingDetection } = options;
390
+ const info = {};
391
+ // Get current user agent
392
+ info.currentUserAgent = navigator.userAgent;
393
+ info.platform = navigator.platform;
394
+ info.language = navigator.language;
395
+ info.languages = navigator.languages;
396
+ // Detect fingerprinting attempts
397
+ if (detectFingerprinting) {
398
+ info.fingerprinting = {
399
+ canvas: !!document.createElement('canvas').getContext,
400
+ webgl: !!document.createElement('canvas').getContext('webgl'),
401
+ audioContext: !!window.AudioContext || !!window.webkitAudioContext,
402
+ webrtc: !!(window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection),
403
+ deviceMemory: navigator.deviceMemory || 'unknown',
404
+ hardwareConcurrency: navigator.hardwareConcurrency || 'unknown',
405
+ maxTouchPoints: navigator.maxTouchPoints || 0
406
+ };
407
+ }
408
+ // Check for custom UA requirements in scripts
409
+ if (findCustomUA) {
410
+ const scripts = document.querySelectorAll('script');
411
+ const uaRequirements = [];
412
+ scripts.forEach(script => {
413
+ const content = script.textContent || '';
414
+ // Look for User-Agent checks
415
+ const uaPatterns = [
416
+ /userAgent\s*[=!]==?\s*['"]([^'"]+)['"]/gi,
417
+ /navigator\.userAgent\.indexOf\(['"]([^'"]+)['"]\)/gi,
418
+ /User-Agent:\s*['"]([^'"]+)['"]/gi
419
+ ];
420
+ uaPatterns.forEach(pattern => {
421
+ const matches = content.match(pattern);
422
+ if (matches) {
423
+ uaRequirements.push(...matches);
424
+ }
425
+ });
426
+ });
427
+ info.customUARequirements = [...new Set(uaRequirements)];
428
+ }
429
+ return info;
430
+ }, { detectFingerprinting, analyzeHeaders, findCustomUA, trackingDetection });
431
+ // Get actual request headers from network
432
+ const requestHeaders = {};
433
+ if (analyzeHeaders) {
434
+ await page.setRequestInterception(true);
435
+ const headerPromise = new Promise((resolve) => {
436
+ page.once('request', (request) => {
437
+ resolve(request.headers());
438
+ request.continue();
439
+ });
440
+ });
441
+ // Trigger a request to capture headers
442
+ await page.reload({ waitUntil: 'domcontentloaded' });
443
+ const headers = await headerPromise;
444
+ requestHeaders.actual = headers;
445
+ await page.setRequestInterception(false);
446
+ }
447
+ return {
448
+ success: true,
449
+ userAgentInfo,
450
+ requestHeaders,
451
+ message: 'User-Agent analysis completed'
452
+ };
453
+ }, 'Failed to analyze User-Agent');
454
+ });
455
+ }
456
+ // Export handlers for all specialized tools
457
+ export const specializedToolsHandlers = {
458
+ links_finders: handleLinksFinders,
459
+ video_play_sources_finder: handleVideoPlaySourcesFinder,
460
+ video_player_hostars_sources_finder: handleVideoPlayerHostarsSourcesFinder,
461
+ ajax_finders: handleAjaxFinders,
462
+ user_agent_finders: handleUserAgentFinders,
463
+ // Add more handlers as needed...
464
+ };
package/dist/index.js CHANGED
@@ -45,6 +45,8 @@ import { handleFullPageScreenshot, handleElementScreenshot, handlePDFGeneration,
45
45
  import { handleRESTAPIEndpointFinder, handleWebhookSupport, handleAllWebsiteAPIFinder } from './handlers/api-integration-handlers.js';
46
46
  // Import video extraction handlers
47
47
  import { handleHtmlElementsExtraction, handleNetworkVideoExtraction, handleVideoSelectorGeneration, handleComprehensiveVideoExtraction, handleURLRedirectTrace } from './handlers/video-extraction-handlers.js';
48
+ // Import specialized tools handlers
49
+ import { handleLinksFinders, handleVideoPlaySourcesFinder, handleVideoPlayerHostarsSourcesFinder, handleAjaxFinders, handleUserAgentFinders } from './handlers/specialized-tools-handlers.js';
48
50
  console.error('🔍 [DEBUG] All modules loaded successfully');
49
51
  console.error(`🔍 [DEBUG] Server info: ${JSON.stringify(SERVER_INFO)}`);
50
52
  console.error(`🔍 [DEBUG] Available tools: ${TOOLS.length} tools loaded`);
@@ -246,6 +248,20 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
246
248
  return await handleComprehensiveVideoExtraction(args);
247
249
  case TOOL_NAMES.URL_REDIRECT_TRACE:
248
250
  return await handleURLRedirectTrace(args);
251
+ // Specialized Video and Links Extraction Tools
252
+ case TOOL_NAMES.LINKS_FINDERS:
253
+ return await handleLinksFinders(args);
254
+ case TOOL_NAMES.VIDEO_PLAY_SOURCES_FINDER:
255
+ return await handleVideoPlaySourcesFinder(args);
256
+ case TOOL_NAMES.VIDEO_PLAYER_HOSTARS_SOURCES_FINDER:
257
+ return await handleVideoPlayerHostarsSourcesFinder(args);
258
+ case TOOL_NAMES.AJAX_FINDERS:
259
+ return await handleAjaxFinders(args);
260
+ case TOOL_NAMES.USER_AGENT_FINDERS:
261
+ return await handleUserAgentFinders(args);
262
+ // Regex Pattern Finders (alias for existing regex_pattern_matcher)
263
+ case TOOL_NAMES.REGEX_PATTERN_FINDERS:
264
+ return await handleRegexPatternMatcher(args);
249
265
  default:
250
266
  throw new Error(`Unknown tool: ${name}`);
251
267
  }
@@ -1106,6 +1106,435 @@ export const TOOLS = [
1106
1106
  required: ['url'],
1107
1107
  },
1108
1108
  },
1109
+ // Specialized Video and Links Extraction Tools
1110
+ {
1111
+ name: 'links_finders',
1112
+ description: 'Find and extract all types of links from page (HTTP, HTTPS, FTP, email, phone, etc.)',
1113
+ inputSchema: {
1114
+ type: 'object',
1115
+ properties: {
1116
+ includeExternal: {
1117
+ type: 'boolean',
1118
+ description: 'Include external links',
1119
+ default: true
1120
+ },
1121
+ includeInternal: {
1122
+ type: 'boolean',
1123
+ description: 'Include internal links',
1124
+ default: true
1125
+ },
1126
+ includeMediaLinks: {
1127
+ type: 'boolean',
1128
+ description: 'Include media file links',
1129
+ default: true
1130
+ },
1131
+ includeEmailPhone: {
1132
+ type: 'boolean',
1133
+ description: 'Include email and phone links',
1134
+ default: false
1135
+ },
1136
+ filterDomains: {
1137
+ type: 'array',
1138
+ description: 'Filter links by specific domains',
1139
+ items: { type: 'string' }
1140
+ }
1141
+ }
1142
+ }
1143
+ },
1144
+ {
1145
+ name: 'video_play_sources_finder',
1146
+ description: 'Find direct video play sources and streaming URLs',
1147
+ inputSchema: {
1148
+ type: 'object',
1149
+ properties: {
1150
+ includeHLS: {
1151
+ type: 'boolean',
1152
+ description: 'Include HLS (.m3u8) sources',
1153
+ default: true
1154
+ },
1155
+ includeDASH: {
1156
+ type: 'boolean',
1157
+ description: 'Include DASH (.mpd) sources',
1158
+ default: true
1159
+ },
1160
+ includeDirectMP4: {
1161
+ type: 'boolean',
1162
+ description: 'Include direct MP4 sources',
1163
+ default: true
1164
+ },
1165
+ includeBlob: {
1166
+ type: 'boolean',
1167
+ description: 'Include blob URLs',
1168
+ default: true
1169
+ },
1170
+ recordingDuration: {
1171
+ type: 'number',
1172
+ description: 'Network recording duration (ms)',
1173
+ default: 15000
1174
+ }
1175
+ }
1176
+ }
1177
+ },
1178
+ {
1179
+ name: 'video_player_hostars_sources_finder',
1180
+ description: 'Find video sources from popular hosting platforms (YouTube, Vimeo, Dailymotion, etc.)',
1181
+ inputSchema: {
1182
+ type: 'object',
1183
+ properties: {
1184
+ platforms: {
1185
+ type: 'array',
1186
+ description: 'Specific platforms to search',
1187
+ items: {
1188
+ type: 'string',
1189
+ enum: ['youtube', 'vimeo', 'dailymotion', 'twitch', 'facebook', 'instagram', 'tiktok', 'all']
1190
+ },
1191
+ default: ['all']
1192
+ },
1193
+ extractEmbedCodes: {
1194
+ type: 'boolean',
1195
+ description: 'Extract embed codes',
1196
+ default: true
1197
+ },
1198
+ extractAPIKeys: {
1199
+ type: 'boolean',
1200
+ description: 'Extract API keys from network requests',
1201
+ default: false
1202
+ }
1203
+ }
1204
+ }
1205
+ },
1206
+ {
1207
+ name: 'video_sources_links_finders',
1208
+ description: 'Comprehensive video source links extraction from all possible locations',
1209
+ inputSchema: {
1210
+ type: 'object',
1211
+ properties: {
1212
+ searchInJS: {
1213
+ type: 'boolean',
1214
+ description: 'Search in JavaScript code',
1215
+ default: true
1216
+ },
1217
+ searchInJSON: {
1218
+ type: 'boolean',
1219
+ description: 'Search in JSON responses',
1220
+ default: true
1221
+ },
1222
+ searchInCSS: {
1223
+ type: 'boolean',
1224
+ description: 'Search in CSS files',
1225
+ default: false
1226
+ },
1227
+ searchInComments: {
1228
+ type: 'boolean',
1229
+ description: 'Search in HTML comments',
1230
+ default: true
1231
+ },
1232
+ deepScan: {
1233
+ type: 'boolean',
1234
+ description: 'Enable deep scanning mode',
1235
+ default: true
1236
+ }
1237
+ }
1238
+ }
1239
+ },
1240
+ {
1241
+ name: 'video_download_page',
1242
+ description: 'Find and navigate to video download pages',
1243
+ inputSchema: {
1244
+ type: 'object',
1245
+ properties: {
1246
+ searchKeywords: {
1247
+ type: 'array',
1248
+ description: 'Keywords to identify download pages',
1249
+ items: { type: 'string' },
1250
+ default: ['download', 'save', 'get', 'grab']
1251
+ },
1252
+ followRedirects: {
1253
+ type: 'boolean',
1254
+ description: 'Follow redirects to find actual download pages',
1255
+ default: true
1256
+ },
1257
+ maxDepth: {
1258
+ type: 'number',
1259
+ description: 'Maximum navigation depth',
1260
+ default: 3
1261
+ }
1262
+ }
1263
+ }
1264
+ },
1265
+ {
1266
+ name: 'video_sources_extracts',
1267
+ description: 'Extract and process video sources with metadata',
1268
+ inputSchema: {
1269
+ type: 'object',
1270
+ properties: {
1271
+ includeMetadata: {
1272
+ type: 'boolean',
1273
+ description: 'Include video metadata (duration, resolution, codec)',
1274
+ default: true
1275
+ },
1276
+ validateSources: {
1277
+ type: 'boolean',
1278
+ description: 'Validate source URLs accessibility',
1279
+ default: false
1280
+ },
1281
+ extractThumbnails: {
1282
+ type: 'boolean',
1283
+ description: 'Extract thumbnail URLs',
1284
+ default: true
1285
+ },
1286
+ extractSubtitles: {
1287
+ type: 'boolean',
1288
+ description: 'Extract subtitle tracks',
1289
+ default: true
1290
+ }
1291
+ }
1292
+ }
1293
+ },
1294
+ {
1295
+ name: 'video_download_button',
1296
+ description: 'Find and interact with video download buttons',
1297
+ inputSchema: {
1298
+ type: 'object',
1299
+ properties: {
1300
+ buttonSelectors: {
1301
+ type: 'array',
1302
+ description: 'Custom button selectors to search',
1303
+ items: { type: 'string' }
1304
+ },
1305
+ clickButton: {
1306
+ type: 'boolean',
1307
+ description: 'Automatically click found download buttons',
1308
+ default: false
1309
+ },
1310
+ waitAfterClick: {
1311
+ type: 'number',
1312
+ description: 'Wait time after clicking (ms)',
1313
+ default: 3000
1314
+ }
1315
+ }
1316
+ }
1317
+ },
1318
+ {
1319
+ name: 'video_play_push_sources',
1320
+ description: 'Find video sources from push/streaming services',
1321
+ inputSchema: {
1322
+ type: 'object',
1323
+ properties: {
1324
+ includeWebSocket: {
1325
+ type: 'boolean',
1326
+ description: 'Include WebSocket streaming sources',
1327
+ default: true
1328
+ },
1329
+ includeWebRTC: {
1330
+ type: 'boolean',
1331
+ description: 'Include WebRTC sources',
1332
+ default: true
1333
+ },
1334
+ includePush: {
1335
+ type: 'boolean',
1336
+ description: 'Include server push sources',
1337
+ default: true
1338
+ },
1339
+ monitoringDuration: {
1340
+ type: 'number',
1341
+ description: 'Duration to monitor for push sources (ms)',
1342
+ default: 20000
1343
+ }
1344
+ }
1345
+ }
1346
+ },
1347
+ {
1348
+ name: 'original_video_hosters_finder',
1349
+ description: 'Find original video hosting sources bypassing CDNs',
1350
+ inputSchema: {
1351
+ type: 'object',
1352
+ properties: {
1353
+ bypassCDN: {
1354
+ type: 'boolean',
1355
+ description: 'Attempt to bypass CDN and find original sources',
1356
+ default: true
1357
+ },
1358
+ traceOrigin: {
1359
+ type: 'boolean',
1360
+ description: 'Trace back to original hosting domain',
1361
+ default: true
1362
+ },
1363
+ includeBackupSources: {
1364
+ type: 'boolean',
1365
+ description: 'Include backup/mirror sources',
1366
+ default: true
1367
+ },
1368
+ deepNetworkAnalysis: {
1369
+ type: 'boolean',
1370
+ description: 'Perform deep network analysis',
1371
+ default: true
1372
+ }
1373
+ }
1374
+ }
1375
+ },
1376
+ {
1377
+ name: 'ajax_finders',
1378
+ description: 'Find and monitor AJAX requests and endpoints',
1379
+ inputSchema: {
1380
+ type: 'object',
1381
+ properties: {
1382
+ interceptXHR: {
1383
+ type: 'boolean',
1384
+ description: 'Intercept XMLHttpRequest calls',
1385
+ default: true
1386
+ },
1387
+ interceptFetch: {
1388
+ type: 'boolean',
1389
+ description: 'Intercept Fetch API calls',
1390
+ default: true
1391
+ },
1392
+ recordResponses: {
1393
+ type: 'boolean',
1394
+ description: 'Record AJAX response data',
1395
+ default: true
1396
+ },
1397
+ filterByContentType: {
1398
+ type: 'array',
1399
+ description: 'Filter by response content types',
1400
+ items: { type: 'string' },
1401
+ default: ['application/json', 'text/html', 'application/xml']
1402
+ },
1403
+ monitoringDuration: {
1404
+ type: 'number',
1405
+ description: 'Duration to monitor AJAX calls (ms)',
1406
+ default: 10000
1407
+ }
1408
+ }
1409
+ }
1410
+ },
1411
+ {
1412
+ name: 'ajax_extracts',
1413
+ description: 'Extract and process data from AJAX responses',
1414
+ inputSchema: {
1415
+ type: 'object',
1416
+ properties: {
1417
+ parseJSON: {
1418
+ type: 'boolean',
1419
+ description: 'Parse JSON responses',
1420
+ default: true
1421
+ },
1422
+ parseXML: {
1423
+ type: 'boolean',
1424
+ description: 'Parse XML responses',
1425
+ default: true
1426
+ },
1427
+ extractVideoSources: {
1428
+ type: 'boolean',
1429
+ description: 'Extract video sources from AJAX responses',
1430
+ default: true
1431
+ },
1432
+ extractAPIEndpoints: {
1433
+ type: 'boolean',
1434
+ description: 'Extract API endpoints from responses',
1435
+ default: true
1436
+ },
1437
+ saveRawData: {
1438
+ type: 'boolean',
1439
+ description: 'Save raw response data',
1440
+ default: false
1441
+ }
1442
+ }
1443
+ }
1444
+ },
1445
+ {
1446
+ name: 'user_agent_finders',
1447
+ description: 'Find and analyze User-Agent strings and browser fingerprinting',
1448
+ inputSchema: {
1449
+ type: 'object',
1450
+ properties: {
1451
+ detectFingerprinting: {
1452
+ type: 'boolean',
1453
+ description: 'Detect browser fingerprinting attempts',
1454
+ default: true
1455
+ },
1456
+ analyzeHeaders: {
1457
+ type: 'boolean',
1458
+ description: 'Analyze all browser headers',
1459
+ default: true
1460
+ },
1461
+ findCustomUA: {
1462
+ type: 'boolean',
1463
+ description: 'Find custom User-Agent requirements',
1464
+ default: true
1465
+ },
1466
+ trackingDetection: {
1467
+ type: 'boolean',
1468
+ description: 'Detect tracking mechanisms',
1469
+ default: true
1470
+ }
1471
+ }
1472
+ }
1473
+ },
1474
+ {
1475
+ name: 'user_agent_extracts',
1476
+ description: 'Extract and generate User-Agent strings for bypassing restrictions',
1477
+ inputSchema: {
1478
+ type: 'object',
1479
+ properties: {
1480
+ generateMobile: {
1481
+ type: 'boolean',
1482
+ description: 'Generate mobile User-Agent strings',
1483
+ default: true
1484
+ },
1485
+ generateDesktop: {
1486
+ type: 'boolean',
1487
+ description: 'Generate desktop User-Agent strings',
1488
+ default: true
1489
+ },
1490
+ generateBot: {
1491
+ type: 'boolean',
1492
+ description: 'Generate bot/crawler User-Agent strings',
1493
+ default: false
1494
+ },
1495
+ targetPlatforms: {
1496
+ type: 'array',
1497
+ description: 'Target specific platforms',
1498
+ items: {
1499
+ type: 'string',
1500
+ enum: ['windows', 'mac', 'linux', 'android', 'ios']
1501
+ },
1502
+ default: ['windows', 'mac', 'android']
1503
+ }
1504
+ }
1505
+ }
1506
+ },
1507
+ {
1508
+ name: 'regex_pattern_finders',
1509
+ description: 'Advanced regex pattern finding and matching (alias for regex_pattern_matcher)',
1510
+ inputSchema: {
1511
+ type: 'object',
1512
+ properties: {
1513
+ patterns: {
1514
+ type: 'array',
1515
+ description: 'Array of regex patterns to search',
1516
+ items: { type: 'string' }
1517
+ },
1518
+ flags: {
1519
+ type: 'string',
1520
+ description: 'Regex flags (g, i, m, s, u, y)',
1521
+ default: 'gi'
1522
+ },
1523
+ extractGroups: {
1524
+ type: 'boolean',
1525
+ description: 'Extract capture groups from matches',
1526
+ default: true
1527
+ },
1528
+ searchScope: {
1529
+ type: 'string',
1530
+ enum: ['html', 'text', 'scripts', 'network', 'all'],
1531
+ description: 'Scope of search',
1532
+ default: 'all'
1533
+ }
1534
+ },
1535
+ required: ['patterns']
1536
+ }
1537
+ },
1109
1538
  ];
1110
1539
  // Tool name constants for type safety
1111
1540
  export const TOOL_NAMES = {
@@ -1189,6 +1618,21 @@ export const TOOL_NAMES = {
1189
1618
  VIDEO_SELECTOR_GENERATION: 'video_selector_generation',
1190
1619
  COMPREHENSIVE_VIDEO_EXTRACTION: 'comprehensive_video_extraction',
1191
1620
  URL_REDIRECT_TRACE: 'url_redirect_trace',
1621
+ // Specialized Video and Links Extraction Tools
1622
+ LINKS_FINDERS: 'links_finders',
1623
+ VIDEO_PLAY_SOURCES_FINDER: 'video_play_sources_finder',
1624
+ VIDEO_PLAYER_HOSTARS_SOURCES_FINDER: 'video_player_hostars_sources_finder',
1625
+ VIDEO_SOURCES_LINKS_FINDERS: 'video_sources_links_finders',
1626
+ VIDEO_DOWNLOAD_PAGE: 'video_download_page',
1627
+ VIDEO_SOURCES_EXTRACTS: 'video_sources_extracts',
1628
+ VIDEO_DOWNLOAD_BUTTON: 'video_download_button',
1629
+ VIDEO_PLAY_PUSH_SOURCES: 'video_play_push_sources',
1630
+ ORIGINAL_VIDEO_HOSTERS_FINDER: 'original_video_hosters_finder',
1631
+ AJAX_FINDERS: 'ajax_finders',
1632
+ AJAX_EXTRACTS: 'ajax_extracts',
1633
+ USER_AGENT_FINDERS: 'user_agent_finders',
1634
+ USER_AGENT_EXTRACTS: 'user_agent_extracts',
1635
+ REGEX_PATTERN_FINDERS: 'regex_pattern_finders',
1192
1636
  };
1193
1637
  // Tool categories for organization
1194
1638
  export const TOOL_CATEGORIES = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brave-real-browser-mcp-server",
3
- "version": "2.9.9",
3
+ "version": "2.9.10",
4
4
  "description": "MCP server for brave-real-browser",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",