brave-real-browser-mcp-server 2.15.3 → 2.15.4

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.
@@ -126,110 +126,3 @@ export async function handleContentClassification(args) {
126
126
  return { content: [{ type: 'text', text: `❌ Error: ${error.message}` }], isError: true };
127
127
  }
128
128
  }
129
- /**
130
- * Sentiment Analysis - Analyze sentiment of page content (Basic Heuristic)
131
- */
132
- export async function handleSentimentAnalysis(args) {
133
- try {
134
- let textToAnalyze = args.text || '';
135
- if (args.url || args.selector) {
136
- const page = getPageInstance();
137
- if (!page)
138
- throw new Error('Browser not initialized');
139
- if (args.url && page.url() !== args.url) {
140
- await page.goto(args.url, { waitUntil: 'domcontentloaded' });
141
- }
142
- if (args.selector) {
143
- textToAnalyze = await page.evaluate((sel) => document.querySelector(sel)?.textContent || '', args.selector);
144
- }
145
- else if (!textToAnalyze) {
146
- textToAnalyze = await page.evaluate(() => document.body.innerText);
147
- }
148
- }
149
- if (!textToAnalyze)
150
- throw new Error('No text to analyze');
151
- // Simple Bag of Words
152
- const positiveWords = ['good', 'great', 'awesome', 'excellent', 'happy', 'love', 'best', 'wonderful', 'amazing'];
153
- const negativeWords = ['bad', 'terrible', 'awful', 'worst', 'hate', 'sad', 'poor', 'disappointing', 'fail'];
154
- const lowerText = textToAnalyze.toLowerCase();
155
- let score = 0;
156
- let matchCount = 0;
157
- positiveWords.forEach(w => {
158
- const regex = new RegExp(`\\b${w}\\b`, 'g');
159
- const count = (lowerText.match(regex) || []).length;
160
- score += count;
161
- matchCount += count;
162
- });
163
- negativeWords.forEach(w => {
164
- const regex = new RegExp(`\\b${w}\\b`, 'g');
165
- const count = (lowerText.match(regex) || []).length;
166
- score -= count;
167
- matchCount += count;
168
- });
169
- let sentiment = 'Neutral';
170
- if (score > 0)
171
- sentiment = 'Positive';
172
- if (score < 0)
173
- sentiment = 'Negative';
174
- return {
175
- content: [{
176
- type: 'text',
177
- text: JSON.stringify({ sentiment, score, matchCount, analyzedLength: textToAnalyze.length }, null, 2)
178
- }]
179
- };
180
- }
181
- catch (error) {
182
- return { content: [{ type: 'text', text: `❌ Error: ${error.message}` }], isError: true };
183
- }
184
- }
185
- /**
186
- * Summary Generator - Generate summary of page content (Basic Truncation/Extraction)
187
- */
188
- export async function handleSummaryGenerator(args) {
189
- try {
190
- let textToSummary = args.text || '';
191
- if (args.url || args.selector) {
192
- const page = getPageInstance();
193
- if (!page)
194
- throw new Error('Browser not initialized');
195
- if (args.url && page.url() !== args.url) {
196
- await page.goto(args.url, { waitUntil: 'domcontentloaded' });
197
- }
198
- if (args.selector) {
199
- textToSummary = await page.evaluate((sel) => document.querySelector(sel)?.textContent || '', args.selector);
200
- }
201
- else if (!textToSummary) {
202
- // Heuristic: Get paragraphs
203
- textToSummary = await page.evaluate(() => {
204
- return Array.from(document.querySelectorAll('p')).map(p => p.textContent).join('\n\n');
205
- });
206
- }
207
- }
208
- if (!textToSummary)
209
- throw new Error('No text to summarize');
210
- // Basic Summary: First 5 sentences or maxLength
211
- const sentences = textToSummary.split(/[.!?]+/).filter(s => s.trim().length > 20);
212
- const summary = sentences.slice(0, 5).join('. ') + '.';
213
- const finalSummary = args.maxLength ? summary.slice(0, args.maxLength) : summary;
214
- return {
215
- content: [{
216
- type: 'text',
217
- text: JSON.stringify({ summary: finalSummary, originalLength: textToSummary.length }, null, 2)
218
- }]
219
- };
220
- }
221
- catch (error) {
222
- return { content: [{ type: 'text', text: `❌ Error: ${error.message}` }], isError: true };
223
- }
224
- }
225
- /**
226
- * Translation Support - Placeholder
227
- */
228
- export async function handleTranslationSupport(args) {
229
- return {
230
- content: [{
231
- type: 'text',
232
- text: `⚠️ Translation Support requires an external API (e.g., Google Translate, DeepL). This feature is defined but currently running in 'offline' mode. To implement, valid API keys would be required.\n\nInput extracted: ${args.text ? 'Yes' : 'No'}\nTarget Language: ${args.targetLanguage}`
233
- }]
234
- };
235
- }
@@ -144,114 +144,6 @@ export async function handleDataTypeValidator(args) {
144
144
  };
145
145
  }
146
146
  }
147
- /**
148
- * Outlier Detection - Detect outliers in numerical data
149
- */
150
- export async function handleOutlierDetection(args) {
151
- const { data, field, method = 'iqr', threshold = 1.5 } = args;
152
- try {
153
- if (!Array.isArray(data)) {
154
- throw new Error('Data must be an array');
155
- }
156
- // Extract numerical values
157
- const values = data
158
- .map(item => {
159
- const value = field ? item[field] : item;
160
- return typeof value === 'number' ? value : parseFloat(value);
161
- })
162
- .filter(v => !isNaN(v));
163
- if (values.length === 0) {
164
- throw new Error('No valid numerical values found');
165
- }
166
- let outliers = [];
167
- let stats = {};
168
- // Sort values
169
- const sorted = [...values].sort((a, b) => a - b);
170
- // Calculate statistics
171
- const mean = values.reduce((a, b) => a + b, 0) / values.length;
172
- const variance = values.reduce((a, b) => a + Math.pow(b - mean, 2), 0) / values.length;
173
- const stdDev = Math.sqrt(variance);
174
- const q1Index = Math.floor(sorted.length * 0.25);
175
- const q3Index = Math.floor(sorted.length * 0.75);
176
- const q1 = sorted[q1Index];
177
- const q3 = sorted[q3Index];
178
- const iqr = q3 - q1;
179
- stats = {
180
- count: values.length,
181
- min: sorted[0],
182
- max: sorted[sorted.length - 1],
183
- mean: mean.toFixed(2),
184
- median: sorted[Math.floor(sorted.length / 2)],
185
- stdDev: stdDev.toFixed(2),
186
- q1,
187
- q3,
188
- iqr: iqr.toFixed(2)
189
- };
190
- // Detect outliers based on method
191
- if (method === 'iqr') {
192
- const lowerBound = q1 - threshold * iqr;
193
- const upperBound = q3 + threshold * iqr;
194
- data.forEach((item, index) => {
195
- const value = field ? item[field] : item;
196
- const numValue = typeof value === 'number' ? value : parseFloat(value);
197
- if (!isNaN(numValue) && (numValue < lowerBound || numValue > upperBound)) {
198
- outliers.push({
199
- item,
200
- index,
201
- value: numValue,
202
- reason: numValue < lowerBound ? 'below lower bound' : 'above upper bound',
203
- bounds: { lower: lowerBound.toFixed(2), upper: upperBound.toFixed(2) }
204
- });
205
- }
206
- });
207
- }
208
- else if (method === 'zscore') {
209
- data.forEach((item, index) => {
210
- const value = field ? item[field] : item;
211
- const numValue = typeof value === 'number' ? value : parseFloat(value);
212
- if (!isNaN(numValue)) {
213
- const zscore = (numValue - mean) / stdDev;
214
- if (Math.abs(zscore) > threshold) {
215
- outliers.push({
216
- item,
217
- index,
218
- value: numValue,
219
- zscore: zscore.toFixed(2),
220
- reason: `zscore ${zscore > 0 ? 'above' : 'below'} threshold`
221
- });
222
- }
223
- }
224
- });
225
- }
226
- const outlierPercentage = ((outliers.length / data.length) * 100).toFixed(2);
227
- let summary = `Outlier Detection Results:\n\nMethod: ${method}\nThreshold: ${threshold}${field ? `\nField Analyzed: ${field}` : ''}\n\nStatistics:\n- Count: ${stats.count}\n- Min: ${stats.min}\n- Max: ${stats.max}\n- Mean: ${stats.mean}\n- Median: ${stats.median}\n- Std Dev: ${stats.stdDev}\n- Q1: ${stats.q1}\n- Q3: ${stats.q3}\n- IQR: ${stats.iqr}`;
228
- summary += `\n\nOutliers:\n- Count: ${outliers.length}\n- Percentage: ${outlierPercentage}%`;
229
- if (outliers.length > 0) {
230
- summary += `\n\nOutlier Samples (Top 10):\n${outliers.slice(0, 10).map((o, i) => {
231
- return `${i + 1}. Index ${o.index}: Value=${o.value}, Reason: ${o.reason}${o.zscore ? `, Z-score=${o.zscore}` : ''}${o.bounds ? `, Bounds=[${o.bounds.lower}, ${o.bounds.upper}]` : ''}`;
232
- }).join('\n')}`;
233
- }
234
- return {
235
- content: [
236
- {
237
- type: "text",
238
- text: summary
239
- }
240
- ]
241
- };
242
- }
243
- catch (error) {
244
- return {
245
- content: [
246
- {
247
- type: "text",
248
- text: `Outlier Detection Error: ${error.message}`
249
- }
250
- ],
251
- isError: true
252
- };
253
- }
254
- }
255
147
  /**
256
148
  * Consistency Checker - Check data consistency across fields
257
149
  */
@@ -2,8 +2,6 @@
2
2
  import { getPageInstance } from '../browser-manager.js';
3
3
  import * as fs from 'fs/promises';
4
4
  import * as path from 'path';
5
- import pixelmatch from 'pixelmatch';
6
- import { PNG } from 'pngjs';
7
5
  import { sleep } from '../system-utils.js';
8
6
  /**
9
7
  * Full Page Screenshot - Capture entire page
@@ -205,63 +203,3 @@ export async function handleVideoRecording(args) {
205
203
  };
206
204
  }
207
205
  }
208
- /**
209
- * Visual Comparison - Compare two screenshots
210
- */
211
- export async function handleVisualComparison(args) {
212
- const { image1Path, image2Path, diffOutputPath, threshold = 0.1 } = args;
213
- try {
214
- if (!image1Path || !image2Path) {
215
- throw new Error('Both image paths are required');
216
- }
217
- // Read images
218
- const img1Data = await fs.readFile(image1Path);
219
- const img2Data = await fs.readFile(image2Path);
220
- const img1 = PNG.sync.read(img1Data);
221
- const img2 = PNG.sync.read(img2Data);
222
- // Check if dimensions match
223
- if (img1.width !== img2.width || img1.height !== img2.height) {
224
- return {
225
- content: [
226
- {
227
- type: "text",
228
- text: `Image dimensions do not match:\n- Image 1: ${img1.width}x${img1.height}\n- Image 2: ${img2.width}x${img2.height}`
229
- }
230
- ],
231
- isError: true
232
- };
233
- }
234
- // Create diff image
235
- const diff = new PNG({ width: img1.width, height: img1.height });
236
- // Compare images
237
- const numDiffPixels = pixelmatch(img1.data, img2.data, diff.data, img1.width, img1.height, { threshold });
238
- // Save diff image if path provided
239
- if (diffOutputPath) {
240
- const dir = path.dirname(diffOutputPath);
241
- await fs.mkdir(dir, { recursive: true });
242
- await fs.writeFile(diffOutputPath, PNG.sync.write(diff));
243
- }
244
- const totalPixels = img1.width * img1.height;
245
- const diffPercentage = (numDiffPixels / totalPixels) * 100;
246
- const similarity = ((1 - (numDiffPixels / totalPixels)) * 100).toFixed(2);
247
- return {
248
- content: [
249
- {
250
- type: "text",
251
- text: `Visual Comparison Results:\n- Identical: ${numDiffPixels === 0 ? 'Yes' : 'No'}\n- Similarity: ${similarity}%\n- Different Pixels: ${numDiffPixels} (${diffPercentage.toFixed(2)}%)\n- Total Pixels: ${totalPixels}\n- Image Dimensions: ${img1.width}x${img1.height}\n- Threshold: ${threshold}${diffOutputPath ? `\n- Diff Image Saved: ${diffOutputPath}` : ''}`
252
- }
253
- ]
254
- };
255
- }
256
- catch (error) {
257
- return {
258
- content: [
259
- {
260
- type: "text",
261
- text: `Visual Comparison Error: ${error.message}`
262
- }
263
- ],
264
- isError: true
265
- };
266
- }
267
- }
package/dist/index.js CHANGED
@@ -38,11 +38,11 @@ import { handleSmartSelectorGenerator, handleContentClassification, } from "./ha
38
38
  // Import search & filter handlers
39
39
  import { handleKeywordSearch, handleRegexPatternMatcher, handleXPathSupport, handleAdvancedCSSSelectors, handleVisualElementFinder, } from "./handlers/search-filter-handlers.js";
40
40
  // Import data quality handlers
41
- import { handleDataDeduplication, handleDataTypeValidator, handleOutlierDetection, } from "./handlers/data-quality-handlers.js";
41
+ import { handleDataDeduplication, handleDataTypeValidator, } from "./handlers/data-quality-handlers.js";
42
42
  // Import captcha handlers
43
43
  import { handleOCREngine, handleAudioCaptchaSolver, handlePuzzleCaptchaHandler, } from "./handlers/captcha-handlers.js";
44
44
  // Import visual tools handlers
45
- import { handleFullPageScreenshot, handleElementScreenshot, handleVideoRecording, handleVisualComparison, } from "./handlers/visual-tools-handlers.js";
45
+ import { handleFullPageScreenshot, handleElementScreenshot, handleVideoRecording, } from "./handlers/visual-tools-handlers.js";
46
46
  // Import smart data extractors
47
47
  import { handleHtmlElementsExtractor, handleTagsFinder, handleLinksFinder, handleXpathLinks, handleAjaxExtractor, handleFetchXHR, handleNetworkRecorder, handleRegexPatternFinder, handleIframeExtractor, handleEmbedPageExtractor, handleImageExtractorAdvanced, handleVideoSourceExtractor, handleUrlRedirectTracer, handleUserAgentExtractor, } from "./handlers/smart-data-extractors.js";
48
48
  // Import dynamic session handlers
@@ -213,9 +213,6 @@ export async function executeToolByName(name, args) {
213
213
  case TOOL_NAMES.DATA_TYPE_VALIDATOR:
214
214
  result = await handleDataTypeValidator(args);
215
215
  break;
216
- case TOOL_NAMES.OUTLIER_DETECTION:
217
- result = await handleOutlierDetection(args);
218
- break;
219
216
  // Advanced Captcha Handling
220
217
  case TOOL_NAMES.OCR_ENGINE:
221
218
  result = await handleOCREngine(args);
@@ -236,9 +233,6 @@ export async function executeToolByName(name, args) {
236
233
  case TOOL_NAMES.VIDEO_RECORDING:
237
234
  result = await handleVideoRecording(args);
238
235
  break;
239
- case TOOL_NAMES.VISUAL_COMPARISON:
240
- result = await handleVisualComparison(args);
241
- break;
242
236
  // Smart Data Extractors (Advanced)
243
237
  case "html_elements_extractor":
244
238
  result = await handleHtmlElementsExtractor(args || {});
@@ -619,20 +619,6 @@ export const TOOLS = [
619
619
  required: ['data', 'schema'],
620
620
  },
621
621
  },
622
- {
623
- name: 'outlier_detection',
624
- description: 'Detect outliers in numerical data',
625
- inputSchema: {
626
- type: 'object',
627
- properties: {
628
- data: { type: 'array' },
629
- field: { type: 'string', description: 'Field to analyze' },
630
- method: { type: 'string', enum: ['iqr', 'zscore'], default: 'iqr' },
631
- threshold: { type: 'number', default: 1.5 },
632
- },
633
- required: ['data'],
634
- },
635
- },
636
622
  {
637
623
  name: 'consistency_checker',
638
624
  description: 'Check data consistency across fields',
@@ -730,20 +716,6 @@ export const TOOLS = [
730
716
  required: ['outputPath'],
731
717
  },
732
718
  },
733
- {
734
- name: 'visual_comparison',
735
- description: 'Compare two screenshots',
736
- inputSchema: {
737
- type: 'object',
738
- properties: {
739
- image1Path: { type: 'string' },
740
- image2Path: { type: 'string' },
741
- diffOutputPath: { type: 'string' },
742
- threshold: { type: 'number', default: 0.1 },
743
- },
744
- required: ['image1Path', 'image2Path'],
745
- },
746
- },
747
719
  // Smart Data Extractors (Advanced)
748
720
  {
749
721
  name: 'html_elements_extractor',
@@ -1055,43 +1027,6 @@ export const TOOLS = [
1055
1027
  },
1056
1028
  },
1057
1029
  },
1058
- {
1059
- name: 'sentiment_analysis',
1060
- description: 'Analyze the sentiment of text or page content',
1061
- inputSchema: {
1062
- type: 'object',
1063
- properties: {
1064
- text: { type: 'string', description: 'Text to analyze' },
1065
- url: { type: 'string', description: 'URL to analyze' },
1066
- selector: { type: 'string', description: 'Selector to extract text from' }
1067
- }
1068
- }
1069
- },
1070
- {
1071
- name: 'summary_generator',
1072
- description: 'Generate a summary of text or page content',
1073
- inputSchema: {
1074
- type: 'object',
1075
- properties: {
1076
- text: { type: 'string', description: 'Text to summarize' },
1077
- url: { type: 'string', description: 'URL to summarize' },
1078
- selector: { type: 'string', description: 'Selector to extract text from' },
1079
- maxLength: { type: 'number', description: 'Maximum length of summary' }
1080
- }
1081
- }
1082
- },
1083
- {
1084
- name: 'translation_support',
1085
- description: 'Detect language and translate text',
1086
- inputSchema: {
1087
- type: 'object',
1088
- properties: {
1089
- text: { type: 'string', description: 'Text to translate' },
1090
- url: { type: 'string', description: 'URL to translate content from' },
1091
- targetLanguage: { type: 'string', description: 'Target language code (e.g. "es", "fr")' }
1092
- }
1093
- }
1094
- },
1095
1030
  // Phase 3: Media & Video Tools
1096
1031
  {
1097
1032
  name: 'video_source_extractor',
@@ -1182,9 +1117,6 @@ export const TOOL_NAMES = {
1182
1117
  // AI-Powered Features
1183
1118
  SMART_SELECTOR_GENERATOR: 'smart_selector_generator',
1184
1119
  CONTENT_CLASSIFICATION: 'content_classification',
1185
- SENTIMENT_ANALYSIS: 'sentiment_analysis',
1186
- SUMMARY_GENERATOR: 'summary_generator',
1187
- TRANSLATION_SUPPORT: 'translation_support',
1188
1120
  // Phase 3: Media & Video
1189
1121
  VIDEO_SOURCE_EXTRACTOR: 'video_source_extractor',
1190
1122
  VIDEO_PLAYER_FINDER: 'video_player_finder',
@@ -1198,7 +1130,6 @@ export const TOOL_NAMES = {
1198
1130
  // Data Quality & Validation
1199
1131
  DATA_DEDUPLICATION: 'data_deduplication',
1200
1132
  DATA_TYPE_VALIDATOR: 'data_type_validator',
1201
- OUTLIER_DETECTION: 'outlier_detection',
1202
1133
  // Advanced Captcha Handling
1203
1134
  OCR_ENGINE: 'ocr_engine',
1204
1135
  AUDIO_CAPTCHA_SOLVER: 'audio_captcha_solver',
@@ -1207,7 +1138,6 @@ export const TOOL_NAMES = {
1207
1138
  FULL_PAGE_SCREENSHOT: 'full_page_screenshot',
1208
1139
  ELEMENT_SCREENSHOT: 'element_screenshot',
1209
1140
  VIDEO_RECORDING: 'video_recording',
1210
- VISUAL_COMPARISON: 'visual_comparison',
1211
1141
  };
1212
1142
  // Tool categories for organization
1213
1143
  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.15.3",
3
+ "version": "2.15.4",
4
4
  "description": "Universal AI IDE MCP Server - Auto-detects and supports all AI IDEs (Claude Desktop, Cursor, Windsurf, Cline, Zed, VSCode, Qoder AI, etc.) with Brave browser automation",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",