brave-real-browser-mcp-server 2.10.0 → 2.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -14,7 +14,7 @@ export async function handleAdvancedVideoExtraction(args) {
14
14
  requirePage: true,
15
15
  });
16
16
  const page = getCurrentPage();
17
- const waitTime = args.waitTime || 10000;
17
+ const waitTime = args.waitTime || 20000;
18
18
  // Collect all video-related data
19
19
  const videoData = {
20
20
  directVideoUrls: [],
@@ -183,7 +183,11 @@ export async function handleAdvancedVideoExtraction(args) {
183
183
  'button[data-download]',
184
184
  'a[href*=".mp4"]',
185
185
  'a[href*=".mkv"]',
186
- 'a[class*="download"]'
186
+ 'a[class*="download"]',
187
+ 'a[href*=".webm"]',
188
+ '.download-btn',
189
+ '.btn-download',
190
+ '[class*="download"]'
187
191
  ];
188
192
  downloadSelectors.forEach(selector => {
189
193
  document.querySelectorAll(selector).forEach((el) => {
@@ -387,12 +387,20 @@ export async function handleNetworkRecordingFinder(args) {
387
387
  const page = getCurrentPage();
388
388
  const duration = args.duration || 10000;
389
389
  const filterType = args.filterType || 'video'; // video, audio, media
390
+ const navigateTo = args.navigateTo; // Optional URL to navigate to
391
+ const verbose = args.verbose !== false; // Default true for detailed logging
390
392
  const recordings = [];
393
+ let totalResponses = 0;
394
+ let matchedResponses = 0;
391
395
  const responseHandler = async (response) => {
392
396
  try {
397
+ totalResponses++;
393
398
  const url = response.url();
394
399
  const contentType = response.headers()['content-type'] || '';
395
400
  const resourceType = response.request().resourceType();
401
+ if (verbose && totalResponses % 10 === 0) {
402
+ console.log(`[Network Recording] Processed ${totalResponses} responses, ${matchedResponses} matched`);
403
+ }
396
404
  let shouldRecord = false;
397
405
  if (filterType === 'video' && (contentType.includes('video') || resourceType === 'media')) {
398
406
  shouldRecord = true;
@@ -404,6 +412,10 @@ export async function handleNetworkRecordingFinder(args) {
404
412
  shouldRecord = true;
405
413
  }
406
414
  if (shouldRecord) {
415
+ matchedResponses++;
416
+ if (verbose) {
417
+ console.log(`[Network Recording] āœ… Matched ${filterType}: ${url.substring(0, 100)}`);
418
+ }
407
419
  try {
408
420
  const buffer = await response.buffer();
409
421
  recordings.push({
@@ -428,21 +440,33 @@ export async function handleNetworkRecordingFinder(args) {
428
440
  // Ignore individual response errors
429
441
  }
430
442
  };
443
+ console.log(`[Network Recording] šŸŽ¬ Starting monitoring for ${filterType} (${duration}ms)${navigateTo ? ` + navigating to ${navigateTo}` : ''}`);
431
444
  page.on('response', responseHandler);
445
+ // If navigateTo is provided, navigate first, then wait
446
+ if (navigateTo) {
447
+ try {
448
+ await page.goto(navigateTo, { waitUntil: 'networkidle2', timeout: 30000 });
449
+ console.log(`[Network Recording] āœ… Navigation complete, continuing monitoring...`);
450
+ }
451
+ catch (e) {
452
+ console.log(`[Network Recording] āš ļø Navigation error (continuing anyway): ${e}`);
453
+ }
454
+ }
432
455
  await sleep(duration);
433
456
  page.off('response', responseHandler);
457
+ console.log(`[Network Recording] šŸ›‘ Monitoring stopped. Total: ${totalResponses}, Matched: ${matchedResponses}, Recorded: ${recordings.length}`);
434
458
  if (recordings.length === 0) {
435
459
  return {
436
460
  content: [{
437
461
  type: 'text',
438
- text: `ā„¹ļø No ${filterType} recordings found during ${duration}ms monitoring period.\n\nšŸ’” Note: Network monitoring starts AFTER this tool is called. To capture video/media requests:\n 1. Start monitoring before navigating to the page\n 2. Or trigger video playback after monitoring starts\n 3. Consider using 'advanced_video_extraction' for comprehensive detection`,
462
+ text: `ā„¹ļø No ${filterType} recordings found\n\nšŸ“Š Statistics:\n • Total responses checked: ${totalResponses}\n • Matched ${filterType} responses: ${matchedResponses}\n • Duration: ${duration}ms\n • Navigation: ${navigateTo || 'None'}\n\nšŸ’” Suggestions:\n ${navigateTo ? '• Try longer duration if page loads slowly\n • Check if page actually has video/media content' : '• Use navigateTo parameter to capture requests during page load\n • Example: {"navigateTo": "https://example.com", "duration": 15000}'}\n • Consider 'advanced_video_extraction' for analyzing loaded content`,
439
463
  }],
440
464
  };
441
465
  }
442
466
  return {
443
467
  content: [{
444
468
  type: 'text',
445
- text: `āœ… Network Recordings Found: ${recordings.length}\n\n${JSON.stringify(recordings, null, 2)}`,
469
+ text: `āœ… Network Recordings Found: ${recordings.length}\n\nšŸ“Š Statistics:\n • Total responses: ${totalResponses}\n • Matched: ${matchedResponses}\n • Recorded: ${recordings.length}\n\n${JSON.stringify(recordings, null, 2)}`,
446
470
  }],
447
471
  };
448
472
  }
@@ -476,18 +500,24 @@ export async function handleNetworkRecordingExtractors(args) {
476
500
  }
477
501
  const page = getCurrentPage();
478
502
  const duration = args.duration || 10000;
503
+ const navigateTo = args.navigateTo; // Optional URL to navigate to
504
+ const verbose = args.verbose !== false; // Default true
479
505
  const extractedData = {
480
506
  videos: [],
481
507
  audio: [],
482
508
  manifests: [],
483
509
  apis: [],
484
510
  };
511
+ let totalResponses = 0;
485
512
  const responseHandler = async (response) => {
513
+ totalResponses++;
486
514
  const url = response.url();
487
515
  const contentType = response.headers()['content-type'] || '';
488
516
  try {
489
517
  // Video files
490
518
  if (contentType.includes('video') || url.includes('.mp4') || url.includes('.webm')) {
519
+ if (verbose)
520
+ console.log(`[Extractor] šŸŽ„ Video found: ${url.substring(0, 80)}`);
491
521
  extractedData.videos.push({
492
522
  url,
493
523
  contentType,
@@ -496,6 +526,8 @@ export async function handleNetworkRecordingExtractors(args) {
496
526
  }
497
527
  // Audio files
498
528
  if (contentType.includes('audio') || url.includes('.mp3') || url.includes('.m4a')) {
529
+ if (verbose)
530
+ console.log(`[Extractor] šŸŽµ Audio found: ${url.substring(0, 80)}`);
499
531
  extractedData.audio.push({
500
532
  url,
501
533
  contentType,
@@ -503,6 +535,8 @@ export async function handleNetworkRecordingExtractors(args) {
503
535
  }
504
536
  // Manifest files (HLS, DASH)
505
537
  if (url.includes('.m3u8') || url.includes('.mpd')) {
538
+ if (verbose)
539
+ console.log(`[Extractor] šŸ“œ Manifest found: ${url.substring(0, 80)}`);
506
540
  const text = await response.text();
507
541
  extractedData.manifests.push({
508
542
  url,
@@ -512,6 +546,8 @@ export async function handleNetworkRecordingExtractors(args) {
512
546
  }
513
547
  // API responses with video data
514
548
  if (contentType.includes('json') && (url.includes('video') || url.includes('media'))) {
549
+ if (verbose)
550
+ console.log(`[Extractor] šŸ“” API found: ${url.substring(0, 80)}`);
515
551
  const json = await response.json();
516
552
  extractedData.apis.push({
517
553
  url,
@@ -523,23 +559,35 @@ export async function handleNetworkRecordingExtractors(args) {
523
559
  // Response not available
524
560
  }
525
561
  };
562
+ console.log(`[Extractor] šŸŽ¬ Starting extraction (${duration}ms)${navigateTo ? ` + navigating to ${navigateTo}` : ''}`);
526
563
  page.on('response', responseHandler);
564
+ // If navigateTo is provided, navigate first, then wait
565
+ if (navigateTo) {
566
+ try {
567
+ await page.goto(navigateTo, { waitUntil: 'networkidle2', timeout: 30000 });
568
+ console.log(`[Extractor] āœ… Navigation complete, continuing extraction...`);
569
+ }
570
+ catch (e) {
571
+ console.log(`[Extractor] āš ļø Navigation error (continuing): ${e}`);
572
+ }
573
+ }
527
574
  await sleep(duration);
528
575
  page.off('response', responseHandler);
529
576
  const totalFound = extractedData.videos.length + extractedData.audio.length +
530
577
  extractedData.manifests.length + extractedData.apis.length;
578
+ console.log(`[Extractor] šŸ›‘ Extraction complete. Total responses: ${totalResponses}, Extracted: ${totalFound}`);
531
579
  if (totalFound === 0) {
532
580
  return {
533
581
  content: [{
534
582
  type: 'text',
535
- text: `ā„¹ļø No media content extracted during ${duration}ms monitoring.\n\nšŸ’” Suggestions:\n • Network monitoring captures requests made AFTER the tool starts\n • Try starting monitoring before page navigation\n • Use 'advanced_video_extraction' for analyzing already-loaded content\n • Consider longer duration if content loads slowly`,
583
+ text: `ā„¹ļø No media content extracted\n\nšŸ“Š Statistics:\n • Total responses checked: ${totalResponses}\n • Duration: ${duration}ms\n • Navigation: ${navigateTo || 'None'}\n\nšŸ’” Suggestions:\n ${navigateTo ? '• Try longer duration (15000-20000ms)\n • Verify page actually contains video/media' : '• Add navigateTo parameter: {"navigateTo": "https://example.com", "duration": 15000}'}\n • Use 'advanced_video_extraction' for analyzing loaded content\n • Check browser console logs for detailed monitoring`,
536
584
  }],
537
585
  };
538
586
  }
539
587
  return {
540
588
  content: [{
541
589
  type: 'text',
542
- text: `āœ… Network Recording Extraction Complete\n\nVideos: ${extractedData.videos.length}\nAudio: ${extractedData.audio.length}\nManifests: ${extractedData.manifests.length}\nAPIs: ${extractedData.apis.length}\n\n${JSON.stringify(extractedData, null, 2)}`,
590
+ text: `āœ… Network Recording Extraction Complete\n\nšŸ“Š Results:\n • Videos: ${extractedData.videos.length}\n • Audio: ${extractedData.audio.length}\n • Manifests: ${extractedData.manifests.length}\n • APIs: ${extractedData.apis.length}\n • Total responses: ${totalResponses}\n\n${JSON.stringify(extractedData, null, 2)}`,
543
591
  }],
544
592
  };
545
593
  }
@@ -6,7 +6,7 @@ import { sleep } from '../system-utils.js';
6
6
  * REST API Endpoint Finder - Discover REST API endpoints
7
7
  */
8
8
  export async function handleRESTAPIEndpointFinder(args) {
9
- const { url, analyzeNetworkRequests = true, scanDuration = 5000 } = args;
9
+ const { url, analyzeNetworkRequests = true, scanDuration = 10000 } = args;
10
10
  try {
11
11
  const page = getPageInstance();
12
12
  if (!page) {
@@ -195,7 +195,8 @@ export async function handleExtractJSON(args) {
195
195
  const results = [];
196
196
  // Extract JSON from script tags
197
197
  if (source === 'script' || source === 'all') {
198
- const scripts = document.querySelectorAll('script[type="application/json"], script[type="application/ld+json"]');
198
+ const defaultSelector = selector || 'script[type="application/json"], script[type="application/ld+json"], script';
199
+ const scripts = document.querySelectorAll(defaultSelector);
199
200
  scripts.forEach((script, index) => {
200
201
  try {
201
202
  const content = script.textContent || '';
@@ -346,7 +347,7 @@ export async function handleExtractSchema(args) {
346
347
  });
347
348
  const page = getCurrentPage();
348
349
  const format = args.format || 'all';
349
- const schemaType = args.schemaType;
350
+ const schemaType = args.schemaType || ['WebPage', 'Organization', 'Product', 'BreadcrumbList'];
350
351
  const schemaData = await page.evaluate(({ format, schemaType }) => {
351
352
  const results = [];
352
353
  // Extract JSON-LD
@@ -358,7 +359,9 @@ export async function handleExtractSchema(args) {
358
359
  // Filter by schema type if specified
359
360
  if (schemaType) {
360
361
  const type = data['@type'] || '';
361
- if (!type.toLowerCase().includes(schemaType.toLowerCase())) {
362
+ const types = Array.isArray(schemaType) ? schemaType : [schemaType];
363
+ const typeMatch = types.some(t => type.toLowerCase().includes(t.toLowerCase()));
364
+ if (!typeMatch) {
362
365
  return;
363
366
  }
364
367
  }
@@ -378,8 +381,12 @@ export async function handleExtractSchema(args) {
378
381
  const items = document.querySelectorAll('[itemscope]');
379
382
  items.forEach((item) => {
380
383
  const itemType = item.getAttribute('itemtype') || '';
381
- if (schemaType && !itemType.toLowerCase().includes(schemaType.toLowerCase())) {
382
- return;
384
+ if (schemaType) {
385
+ const types = Array.isArray(schemaType) ? schemaType : [schemaType];
386
+ const typeMatch = types.some(t => itemType.toLowerCase().includes(t.toLowerCase()));
387
+ if (!typeMatch) {
388
+ return;
389
+ }
383
390
  }
384
391
  const properties = {};
385
392
  const props = item.querySelectorAll('[itemprop]');
@@ -184,7 +184,7 @@ export async function handleAjaxExtractor(args) {
184
184
  requirePage: true,
185
185
  });
186
186
  const page = getCurrentPage();
187
- const duration = args.duration || 5000;
187
+ const duration = args.duration || 15000;
188
188
  const url = args.url;
189
189
  const requests = [];
190
190
  const requestHandler = (request) => {
@@ -223,7 +223,7 @@ export async function handleFetchXHR(args) {
223
223
  requirePage: true,
224
224
  });
225
225
  const page = getCurrentPage();
226
- const duration = args.duration || 5000;
226
+ const duration = args.duration || 15000;
227
227
  const xhrData = [];
228
228
  const responseHandler = async (response) => {
229
229
  const request = response.request();
@@ -267,8 +267,8 @@ export async function handleNetworkRecorder(args) {
267
267
  requirePage: true,
268
268
  });
269
269
  const page = getCurrentPage();
270
- const duration = args.duration || 10000;
271
- const filterTypes = args.filterTypes || ['all'];
270
+ const duration = args.duration || 20000;
271
+ const filterTypes = args.filterTypes || ['video', 'xhr', 'fetch', 'media'];
272
272
  const networkActivity = [];
273
273
  const requestHandler = (request) => {
274
274
  const resourceType = request.resourceType();
@@ -317,13 +317,16 @@ export async function handleApiFinder(args) {
317
317
  requirePage: true,
318
318
  });
319
319
  const page = getCurrentPage();
320
- const apis = await page.evaluate(() => {
320
+ const deepScan = args.deepScan !== false;
321
+ const apis = await page.evaluate(({ deepScan }) => {
321
322
  const results = [];
322
323
  const scripts = Array.from(document.querySelectorAll('script'));
323
324
  const apiPatterns = [
324
325
  /https?:\/\/[^"'\s]+\/api\/[^"'\s]*/gi,
325
326
  /https?:\/\/api\.[^"'\s]+/gi,
326
327
  /\/api\/v?\d*\/[^"'\s]*/gi,
328
+ /graphql/gi,
329
+ /rest\/v?\d*/gi,
327
330
  ];
328
331
  scripts.forEach(script => {
329
332
  const content = script.textContent || '';
@@ -337,8 +340,21 @@ export async function handleApiFinder(args) {
337
340
  }
338
341
  });
339
342
  });
343
+ // Deep scan: also check HTML content for API references
344
+ if (deepScan) {
345
+ const htmlContent = document.body.innerHTML;
346
+ apiPatterns.forEach(pattern => {
347
+ const matches = htmlContent.match(pattern);
348
+ if (matches) {
349
+ matches.forEach(match => results.push({
350
+ url: match,
351
+ source: 'html_content',
352
+ }));
353
+ }
354
+ });
355
+ }
340
356
  return [...new Set(results.map(r => r.url))].map(url => ({ url, source: 'script' }));
341
- });
357
+ }, { deepScan });
342
358
  return {
343
359
  content: [{
344
360
  type: 'text',
@@ -942,7 +942,7 @@ export const TOOLS = [
942
942
  properties: {
943
943
  url: { type: 'string' },
944
944
  analyzeNetworkRequests: { type: 'boolean', default: true },
945
- scanDuration: { type: 'number', default: 5000 },
945
+ scanDuration: { type: 'number', default: 10000 },
946
946
  },
947
947
  },
948
948
  },
@@ -1024,7 +1024,7 @@ export const TOOLS = [
1024
1024
  inputSchema: {
1025
1025
  type: 'object',
1026
1026
  properties: {
1027
- duration: { type: 'number', default: 5000 },
1027
+ duration: { type: 'number', default: 15000 },
1028
1028
  url: { type: 'string' },
1029
1029
  },
1030
1030
  },
@@ -1035,7 +1035,7 @@ export const TOOLS = [
1035
1035
  inputSchema: {
1036
1036
  type: 'object',
1037
1037
  properties: {
1038
- duration: { type: 'number', default: 5000 },
1038
+ duration: { type: 'number', default: 15000 },
1039
1039
  },
1040
1040
  },
1041
1041
  },
@@ -1045,8 +1045,8 @@ export const TOOLS = [
1045
1045
  inputSchema: {
1046
1046
  type: 'object',
1047
1047
  properties: {
1048
- duration: { type: 'number', default: 10000 },
1049
- filterTypes: { type: 'array', items: { type: 'string' }, default: ['all'] },
1048
+ duration: { type: 'number', default: 20000 },
1049
+ filterTypes: { type: 'array', items: { type: 'string' }, default: ['video', 'xhr', 'fetch', 'media'] },
1050
1050
  },
1051
1051
  },
1052
1052
  },
@@ -1055,7 +1055,9 @@ export const TOOLS = [
1055
1055
  description: 'Discover API endpoints on page',
1056
1056
  inputSchema: {
1057
1057
  type: 'object',
1058
- properties: {},
1058
+ properties: {
1059
+ deepScan: { type: 'boolean', default: true },
1060
+ },
1059
1061
  },
1060
1062
  },
1061
1063
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brave-real-browser-mcp-server",
3
- "version": "2.10.0",
3
+ "version": "2.11.1",
4
4
  "description": "MCP server for brave-real-browser",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",