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

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/dist/index.js CHANGED
@@ -12,6 +12,7 @@ console.error('šŸ” [DEBUG] Loading tool definitions...');
12
12
  import { TOOLS, SERVER_INFO, CAPABILITIES, TOOL_NAMES } from './tool-definitions.js';
13
13
  console.error('šŸ” [DEBUG] Loading system utils...');
14
14
  import { withErrorHandling } from './system-utils.js';
15
+ import { validateMCPResponse } from './mcp-response-validator.js';
15
16
  console.error('šŸ” [DEBUG] Loading browser manager...');
16
17
  import { closeBrowser, forceKillAllBraveProcesses } from './browser-manager.js';
17
18
  console.error('šŸ” [DEBUG] Loading core infrastructure...');
@@ -51,6 +52,8 @@ import { handleShadowDOMExtractor, handleCookieManager, handleSessionPersistence
51
52
  import { handleProgressTracker, handleErrorLogger, handleSuccessRateReporter, handleDataQualityMetrics, handlePerformanceMonitor, handleGetMonitoringSummary } from './handlers/monitoring-reporting-handlers.js';
52
53
  // Import advanced video & media handlers
53
54
  import { handleVideoLinkFinder, handleVideoDownloadPage, handleVideoDownloadButton, handleVideoPlayPushSource, handleVideoPlayButtonClick, handleUrlRedirectTraceEndpoints, handleNetworkRecordingFinder, handleNetworkRecordingExtractors, handleVideoLinksFinders, handleVideosSelectors, handleLinkProcessExtracts, handleVideoLinkFindersExtracts, handleVideoDownloadButtonFinders } from './handlers/advanced-video-media-handlers.js';
55
+ // Import advanced extraction handlers (Ad-bypass & Obfuscation)
56
+ import { handleAdvancedVideoExtraction, handleDeobfuscateJS, handleMultiLayerRedirectTrace, handleAdProtectionDetector } from './handlers/advanced-extraction-handlers.js';
54
57
  console.error('šŸ” [DEBUG] All modules loaded successfully');
55
58
  console.error(`šŸ” [DEBUG] Server info: ${JSON.stringify(SERVER_INFO)}`);
56
59
  console.error(`šŸ” [DEBUG] Available tools: ${TOOLS.length} tools loaded`);
@@ -104,238 +107,360 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
104
107
  const { name, arguments: args } = request.params;
105
108
  console.error(`šŸ” [DEBUG] Tool call received: ${name} with args: ${JSON.stringify(args)}`);
106
109
  try {
110
+ let result;
107
111
  switch (name) {
108
112
  case TOOL_NAMES.BROWSER_INIT:
109
- return await handleBrowserInit(args || {});
113
+ result = await handleBrowserInit(args || {});
114
+ break;
110
115
  case TOOL_NAMES.NAVIGATE:
111
- return await handleNavigate(args);
116
+ result = await handleNavigate(args);
117
+ break;
112
118
  case TOOL_NAMES.GET_CONTENT:
113
- return await handleGetContent(args || {});
119
+ result = await handleGetContent(args || {});
120
+ break;
114
121
  case TOOL_NAMES.CLICK:
115
- return await handleClick(args);
122
+ result = await handleClick(args);
123
+ break;
116
124
  case TOOL_NAMES.TYPE:
117
- return await handleType(args);
125
+ result = await handleType(args);
126
+ break;
118
127
  case TOOL_NAMES.WAIT:
119
- return await handleWait(args);
128
+ result = await handleWait(args);
129
+ break;
120
130
  case TOOL_NAMES.BROWSER_CLOSE:
121
- return await handleBrowserClose();
131
+ result = await handleBrowserClose();
132
+ break;
122
133
  case TOOL_NAMES.SOLVE_CAPTCHA:
123
- return await handleSolveCaptcha(args);
134
+ result = await handleSolveCaptcha(args);
135
+ break;
124
136
  case TOOL_NAMES.RANDOM_SCROLL:
125
- return await handleRandomScroll();
137
+ result = await handleRandomScroll();
138
+ break;
126
139
  case TOOL_NAMES.FIND_SELECTOR:
127
- return await handleFindSelector(args);
140
+ result = await handleFindSelector(args);
141
+ break;
128
142
  case TOOL_NAMES.SAVE_CONTENT_AS_MARKDOWN:
129
- return await handleSaveContentAsMarkdown(args);
143
+ result = await handleSaveContentAsMarkdown(args);
144
+ break;
130
145
  // Smart Data Extractors
131
146
  case TOOL_NAMES.SCRAPE_TABLE:
132
- return await handleScrapeTable(args || {});
147
+ result = await handleScrapeTable(args || {});
148
+ break;
133
149
  case TOOL_NAMES.EXTRACT_LIST:
134
- return await handleExtractList(args || {});
150
+ result = await handleExtractList(args || {});
151
+ break;
135
152
  case TOOL_NAMES.EXTRACT_JSON:
136
- return await handleExtractJSON(args || {});
153
+ result = await handleExtractJSON(args || {});
154
+ break;
137
155
  case TOOL_NAMES.SCRAPE_META_TAGS:
138
- return await handleScrapeMetaTags(args || {});
156
+ result = await handleScrapeMetaTags(args || {});
157
+ break;
139
158
  case TOOL_NAMES.EXTRACT_SCHEMA:
140
- return await handleExtractSchema(args || {});
159
+ result = await handleExtractSchema(args || {});
160
+ break;
141
161
  // Multi-Element Extractors
142
162
  case TOOL_NAMES.BATCH_ELEMENT_SCRAPER:
143
- return await handleBatchElementScraper(args);
163
+ result = await handleBatchElementScraper(args);
164
+ break;
144
165
  case TOOL_NAMES.NESTED_DATA_EXTRACTION:
145
- return await handleNestedDataExtraction(args);
166
+ result = await handleNestedDataExtraction(args);
167
+ break;
146
168
  case TOOL_NAMES.ATTRIBUTE_HARVESTER:
147
- return await handleAttributeHarvester(args);
169
+ result = await handleAttributeHarvester(args);
170
+ break;
148
171
  // Content Type Specific
149
172
  case TOOL_NAMES.IMAGE_SCRAPER:
150
- return await handleImageScraper(args || {});
173
+ result = await handleImageScraper(args || {});
174
+ break;
151
175
  case TOOL_NAMES.LINK_HARVESTER:
152
- return await handleLinkHarvester(args || {});
176
+ result = await handleLinkHarvester(args || {});
177
+ break;
153
178
  case TOOL_NAMES.MEDIA_EXTRACTOR:
154
- return await handleMediaExtractor(args || {});
179
+ result = await handleMediaExtractor(args || {});
180
+ break;
155
181
  case TOOL_NAMES.PDF_LINK_FINDER:
156
- return await handlePDFLinkFinder(args || {});
182
+ result = await handlePDFLinkFinder(args || {});
183
+ break;
157
184
  // Pagination Tools
158
185
  case TOOL_NAMES.AUTO_PAGINATION:
159
- return await handleAutoPagination(args || {});
186
+ result = await handleAutoPagination(args || {});
187
+ break;
160
188
  case TOOL_NAMES.INFINITE_SCROLL:
161
- return await handleInfiniteScroll(args || {});
189
+ result = await handleInfiniteScroll(args || {});
190
+ break;
162
191
  case TOOL_NAMES.MULTI_PAGE_SCRAPER:
163
- return await handleMultiPageScraper(args);
192
+ result = await handleMultiPageScraper(args);
193
+ break;
164
194
  case TOOL_NAMES.SITEMAP_PARSER:
165
- return await handleSitemapParser(args || {});
195
+ result = await handleSitemapParser(args || {});
196
+ break;
166
197
  case TOOL_NAMES.BREADCRUMB_NAVIGATOR:
167
- return await handleBreadcrumbNavigator(args || {});
198
+ result = await handleBreadcrumbNavigator(args || {});
199
+ break;
168
200
  // Data Processing Tools
169
201
  case TOOL_NAMES.SMART_TEXT_CLEANER:
170
- return await handleSmartTextCleaner(args);
202
+ result = await handleSmartTextCleaner(args);
203
+ break;
171
204
  case TOOL_NAMES.HTML_TO_TEXT:
172
- return await handleHTMLToText(args);
205
+ result = await handleHTMLToText(args);
206
+ break;
173
207
  case TOOL_NAMES.PRICE_PARSER:
174
- return await handlePriceParser(args);
208
+ result = await handlePriceParser(args);
209
+ break;
175
210
  case TOOL_NAMES.DATE_NORMALIZER:
176
- return await handleDateNormalizer(args);
211
+ result = await handleDateNormalizer(args);
212
+ break;
177
213
  case TOOL_NAMES.CONTACT_EXTRACTOR:
178
- return await handleContactExtractor(args);
214
+ result = await handleContactExtractor(args);
215
+ break;
179
216
  // Data Validation Tools
180
217
  case TOOL_NAMES.SCHEMA_VALIDATOR:
181
- return await handleSchemaValidator(args);
218
+ result = await handleSchemaValidator(args);
219
+ break;
182
220
  case TOOL_NAMES.REQUIRED_FIELDS_CHECKER:
183
- return await handleRequiredFieldsChecker(args);
221
+ result = await handleRequiredFieldsChecker(args);
222
+ break;
184
223
  case TOOL_NAMES.DUPLICATE_REMOVER:
185
- return await handleDuplicateRemover(args);
224
+ result = await handleDuplicateRemover(args);
225
+ break;
186
226
  // AI-Powered Features
187
227
  case TOOL_NAMES.SMART_SELECTOR_GENERATOR:
188
- return await handleSmartSelectorGenerator(args);
228
+ result = await handleSmartSelectorGenerator(args);
229
+ break;
189
230
  case TOOL_NAMES.CONTENT_CLASSIFICATION:
190
- return await handleContentClassification(args);
231
+ result = await handleContentClassification(args);
232
+ break;
191
233
  case TOOL_NAMES.SENTIMENT_ANALYSIS:
192
- return await handleSentimentAnalysis(args);
234
+ result = await handleSentimentAnalysis(args);
235
+ break;
193
236
  case TOOL_NAMES.SUMMARY_GENERATOR:
194
- return await handleSummaryGenerator(args);
237
+ result = await handleSummaryGenerator(args);
238
+ break;
195
239
  case TOOL_NAMES.TRANSLATION_SUPPORT:
196
- return await handleTranslationSupport(args);
240
+ result = await handleTranslationSupport(args);
241
+ break;
197
242
  // Search & Filter Tools
198
243
  case TOOL_NAMES.KEYWORD_SEARCH:
199
- return await handleKeywordSearch(args);
244
+ result = await handleKeywordSearch(args);
245
+ break;
200
246
  case TOOL_NAMES.REGEX_PATTERN_MATCHER:
201
- return await handleRegexPatternMatcher(args);
247
+ result = await handleRegexPatternMatcher(args);
248
+ break;
202
249
  case TOOL_NAMES.XPATH_SUPPORT:
203
- return await handleXPathSupport(args);
250
+ result = await handleXPathSupport(args);
251
+ break;
204
252
  case TOOL_NAMES.ADVANCED_CSS_SELECTORS:
205
- return await handleAdvancedCSSSelectors(args);
253
+ result = await handleAdvancedCSSSelectors(args);
254
+ break;
206
255
  case TOOL_NAMES.VISUAL_ELEMENT_FINDER:
207
- return await handleVisualElementFinder(args);
256
+ result = await handleVisualElementFinder(args);
257
+ break;
208
258
  // Data Quality & Validation
209
259
  case TOOL_NAMES.DATA_DEDUPLICATION:
210
- return await handleDataDeduplication(args);
260
+ result = await handleDataDeduplication(args);
261
+ break;
211
262
  case TOOL_NAMES.MISSING_DATA_HANDLER:
212
- return await handleMissingDataHandler(args);
263
+ result = await handleMissingDataHandler(args);
264
+ break;
213
265
  case TOOL_NAMES.DATA_TYPE_VALIDATOR:
214
- return await handleDataTypeValidator(args);
266
+ result = await handleDataTypeValidator(args);
267
+ break;
215
268
  case TOOL_NAMES.OUTLIER_DETECTION:
216
- return await handleOutlierDetection(args);
269
+ result = await handleOutlierDetection(args);
270
+ break;
217
271
  case TOOL_NAMES.CONSISTENCY_CHECKER:
218
- return await handleConsistencyChecker(args);
272
+ result = await handleConsistencyChecker(args);
273
+ break;
219
274
  // Advanced Captcha Handling
220
275
  case TOOL_NAMES.OCR_ENGINE:
221
- return await handleOCREngine(args);
276
+ result = await handleOCREngine(args);
277
+ break;
222
278
  case TOOL_NAMES.AUDIO_CAPTCHA_SOLVER:
223
- return await handleAudioCaptchaSolver(args);
279
+ result = await handleAudioCaptchaSolver(args);
280
+ break;
224
281
  case TOOL_NAMES.PUZZLE_CAPTCHA_HANDLER:
225
- return await handlePuzzleCaptchaHandler(args);
282
+ result = await handlePuzzleCaptchaHandler(args);
283
+ break;
226
284
  // Screenshot & Visual Tools
227
285
  case TOOL_NAMES.FULL_PAGE_SCREENSHOT:
228
- return await handleFullPageScreenshot(args);
286
+ result = await handleFullPageScreenshot(args);
287
+ break;
229
288
  case TOOL_NAMES.ELEMENT_SCREENSHOT:
230
- return await handleElementScreenshot(args);
289
+ result = await handleElementScreenshot(args);
290
+ break;
231
291
  case TOOL_NAMES.PDF_GENERATION:
232
- return await handlePDFGeneration(args);
292
+ result = await handlePDFGeneration(args);
293
+ break;
233
294
  case TOOL_NAMES.VIDEO_RECORDING:
234
- return await handleVideoRecording(args);
295
+ result = await handleVideoRecording(args);
296
+ break;
235
297
  case TOOL_NAMES.VISUAL_COMPARISON:
236
- return await handleVisualComparison(args);
298
+ result = await handleVisualComparison(args);
299
+ break;
237
300
  // Website API Integration
238
301
  case TOOL_NAMES.REST_API_ENDPOINT_FINDER:
239
- return await handleRESTAPIEndpointFinder(args);
302
+ result = await handleRESTAPIEndpointFinder(args);
303
+ break;
240
304
  case TOOL_NAMES.WEBHOOK_SUPPORT:
241
- return await handleWebhookSupport(args);
305
+ result = await handleWebhookSupport(args);
306
+ break;
242
307
  case TOOL_NAMES.ALL_WEBSITE_API_FINDER:
243
- return await handleAllWebsiteAPIFinder(args);
308
+ result = await handleAllWebsiteAPIFinder(args);
309
+ break;
244
310
  // Smart Data Extractors (Advanced)
245
311
  case 'html_elements_extractor':
246
- return await handleHtmlElementsExtractor(args || {});
312
+ result = await handleHtmlElementsExtractor(args || {});
313
+ break;
247
314
  case 'tags_finder':
248
- return await handleTagsFinder(args || {});
315
+ result = await handleTagsFinder(args || {});
316
+ break;
249
317
  case 'links_finder':
250
- return await handleLinksFinder(args || {});
318
+ result = await handleLinksFinder(args || {});
319
+ break;
251
320
  case 'xpath_links':
252
- return await handleXpathLinks(args || {});
321
+ result = await handleXpathLinks(args || {});
322
+ break;
253
323
  case 'ajax_extractor':
254
- return await handleAjaxExtractor(args || {});
324
+ result = await handleAjaxExtractor(args || {});
325
+ break;
255
326
  case 'fetch_xhr':
256
- return await handleFetchXHR(args || {});
327
+ result = await handleFetchXHR(args || {});
328
+ break;
257
329
  case 'network_recorder':
258
- return await handleNetworkRecorder(args || {});
330
+ result = await handleNetworkRecorder(args || {});
331
+ break;
259
332
  case 'api_finder':
260
- return await handleApiFinder(args || {});
333
+ result = await handleApiFinder(args || {});
334
+ break;
261
335
  case 'regex_pattern_finder':
262
- return await handleRegexPatternFinder(args);
336
+ result = await handleRegexPatternFinder(args);
337
+ break;
263
338
  case 'iframe_extractor':
264
- return await handleIframeExtractor(args || {});
339
+ result = await handleIframeExtractor(args || {});
340
+ break;
265
341
  case 'embed_page_extractor':
266
- return await handleEmbedPageExtractor(args || {});
342
+ result = await handleEmbedPageExtractor(args || {});
343
+ break;
267
344
  case 'image_extractor_advanced':
268
- return await handleImageExtractorAdvanced(args || {});
345
+ result = await handleImageExtractorAdvanced(args || {});
346
+ break;
269
347
  case 'video_source_extractor':
270
- return await handleVideoSourceExtractor(args || {});
348
+ result = await handleVideoSourceExtractor(args || {});
349
+ break;
271
350
  case 'video_player_extractor':
272
- return await handleVideoPlayerExtractor(args || {});
351
+ result = await handleVideoPlayerExtractor(args || {});
352
+ break;
273
353
  case 'video_player_hoster_finder':
274
- return await handleVideoPlayerHosterFinder(args || {});
354
+ result = await handleVideoPlayerHosterFinder(args || {});
355
+ break;
275
356
  case 'original_video_hoster_finder':
276
- return await handleOriginalVideoHosterFinder(args || {});
357
+ result = await handleOriginalVideoHosterFinder(args || {});
358
+ break;
277
359
  case 'url_redirect_tracer':
278
- return await handleUrlRedirectTracer(args);
360
+ result = await handleUrlRedirectTracer(args);
361
+ break;
279
362
  case 'user_agent_extractor':
280
- return await handleUserAgentExtractor(args || {});
363
+ result = await handleUserAgentExtractor(args || {});
364
+ break;
281
365
  // Dynamic Content & Session Handling
282
366
  case 'shadow_dom_extractor':
283
- return await handleShadowDOMExtractor(args || {});
367
+ result = await handleShadowDOMExtractor(args || {});
368
+ break;
284
369
  case 'cookie_manager':
285
- return await handleCookieManager(args);
370
+ result = await handleCookieManager(args);
371
+ break;
286
372
  case 'session_persistence':
287
- return await handleSessionPersistence(args);
373
+ result = await handleSessionPersistence(args);
374
+ break;
288
375
  case 'form_auto_fill':
289
- return await handleFormAutoFill(args);
376
+ result = await handleFormAutoFill(args);
377
+ break;
290
378
  case 'ajax_content_waiter':
291
- return await handleAjaxContentWaiter(args);
379
+ result = await handleAjaxContentWaiter(args);
380
+ break;
292
381
  case 'modal_popup_handler':
293
- return await handleModalPopupHandler(args);
382
+ result = await handleModalPopupHandler(args);
383
+ break;
294
384
  case 'login_session_manager':
295
- return await handleLoginSessionManager(args);
385
+ result = await handleLoginSessionManager(args);
386
+ break;
296
387
  // Monitoring & Reporting
297
388
  case 'progress_tracker':
298
- return await handleProgressTracker(args || {});
389
+ result = await handleProgressTracker(args || {});
390
+ break;
299
391
  case 'error_logger':
300
- return await handleErrorLogger(args || {});
392
+ result = await handleErrorLogger(args || {});
393
+ break;
301
394
  case 'success_rate_reporter':
302
- return await handleSuccessRateReporter(args || {});
395
+ result = await handleSuccessRateReporter(args || {});
396
+ break;
303
397
  case 'data_quality_metrics':
304
- return await handleDataQualityMetrics(args || {});
398
+ result = await handleDataQualityMetrics(args || {});
399
+ break;
305
400
  case 'performance_monitor':
306
- return await handlePerformanceMonitor(args || {});
401
+ result = await handlePerformanceMonitor(args || {});
402
+ break;
307
403
  case 'monitoring_summary':
308
- return await handleGetMonitoringSummary(args || {});
404
+ result = await handleGetMonitoringSummary(args || {});
405
+ break;
309
406
  // Advanced Video & Media Download Tools
310
407
  case 'video_link_finder':
311
- return await handleVideoLinkFinder(args || {});
408
+ result = await handleVideoLinkFinder(args || {});
409
+ break;
312
410
  case 'video_download_page':
313
- return await handleVideoDownloadPage(args || {});
411
+ result = await handleVideoDownloadPage(args || {});
412
+ break;
314
413
  case 'video_download_button':
315
- return await handleVideoDownloadButton(args);
414
+ result = await handleVideoDownloadButton(args);
415
+ break;
316
416
  case 'video_play_push_source':
317
- return await handleVideoPlayPushSource(args || {});
417
+ result = await handleVideoPlayPushSource(args || {});
418
+ break;
318
419
  case 'video_play_button_click':
319
- return await handleVideoPlayButtonClick(args || {});
420
+ result = await handleVideoPlayButtonClick(args || {});
421
+ break;
320
422
  case 'url_redirect_trace_endpoints':
321
- return await handleUrlRedirectTraceEndpoints(args);
423
+ result = await handleUrlRedirectTraceEndpoints(args);
424
+ break;
322
425
  case 'network_recording_finder':
323
- return await handleNetworkRecordingFinder(args || {});
426
+ result = await handleNetworkRecordingFinder(args || {});
427
+ break;
324
428
  case 'network_recording_extractors':
325
- return await handleNetworkRecordingExtractors(args || {});
429
+ result = await handleNetworkRecordingExtractors(args || {});
430
+ break;
326
431
  case 'video_links_finders':
327
- return await handleVideoLinksFinders(args || {});
432
+ result = await handleVideoLinksFinders(args || {});
433
+ break;
328
434
  case 'videos_selectors':
329
- return await handleVideosSelectors(args || {});
435
+ result = await handleVideosSelectors(args || {});
436
+ break;
330
437
  case 'link_process_extracts':
331
- return await handleLinkProcessExtracts(args || {});
438
+ result = await handleLinkProcessExtracts(args || {});
439
+ break;
332
440
  case 'video_link_finders_extracts':
333
- return await handleVideoLinkFindersExtracts(args || {});
441
+ result = await handleVideoLinkFindersExtracts(args || {});
442
+ break;
334
443
  case 'video_download_button_finders':
335
- return await handleVideoDownloadButtonFinders(args || {});
444
+ result = await handleVideoDownloadButtonFinders(args || {});
445
+ break;
446
+ // Advanced Extraction Tools (Ad-Bypass & Obfuscation)
447
+ case 'advanced_video_extraction':
448
+ result = await handleAdvancedVideoExtraction(args || {});
449
+ break;
450
+ case 'deobfuscate_js':
451
+ result = await handleDeobfuscateJS(args || {});
452
+ break;
453
+ case 'multi_layer_redirect_trace':
454
+ result = await handleMultiLayerRedirectTrace(args);
455
+ break;
456
+ case 'ad_protection_detector':
457
+ result = await handleAdProtectionDetector(args || {});
458
+ break;
336
459
  default:
337
460
  throw new Error(`Unknown tool: ${name}`);
338
461
  }
462
+ // Validate MCP response format universally
463
+ return validateMCPResponse(result, name);
339
464
  }
340
465
  catch (error) {
341
466
  const errorMessage = error instanceof Error ? error.message : String(error);
@@ -0,0 +1,145 @@
1
+ /**
2
+ * MCP Response Validator
3
+ * Ensures all tool responses conform to MCP SDK standards
4
+ */
5
+ /**
6
+ * Validates and ensures response is in proper MCP format
7
+ */
8
+ export function validateMCPResponse(response, toolName) {
9
+ // If response is null or undefined
10
+ if (!response) {
11
+ console.error(`āš ļø Tool ${toolName} returned null/undefined`);
12
+ return {
13
+ content: [{
14
+ type: 'text',
15
+ text: `āš ļø Tool ${toolName} returned no data. This might indicate:
16
+ • The tool completed but found no results
17
+ • An internal error occurred
18
+ • The page state doesn't support this operation
19
+
20
+ šŸ’” Try: Refresh the page or navigate to the target URL first.`
21
+ }]
22
+ };
23
+ }
24
+ // If response is empty object
25
+ if (typeof response === 'object' && Object.keys(response).length === 0) {
26
+ console.error(`āš ļø Tool ${toolName} returned empty object {}`);
27
+ return {
28
+ content: [{
29
+ type: 'text',
30
+ text: `āš ļø Tool ${toolName} returned empty result. This typically means:
31
+ • No data was found matching the criteria
32
+ • The page needs to be freshly loaded for monitoring tools
33
+ • Network events have already completed
34
+
35
+ šŸ’” Suggestion: For network monitoring tools, start monitoring before navigating to the page.`
36
+ }]
37
+ };
38
+ }
39
+ // If response has content array, validate it
40
+ if (response.content && Array.isArray(response.content)) {
41
+ // Check if content array is empty
42
+ if (response.content.length === 0) {
43
+ console.warn(`āš ļø Tool ${toolName} has empty content array`);
44
+ return {
45
+ content: [{
46
+ type: 'text',
47
+ text: `ā„¹ļø Tool ${toolName} executed successfully but returned no content.`
48
+ }],
49
+ isError: response.isError
50
+ };
51
+ }
52
+ // Validate each content item
53
+ const validContent = response.content.filter((item) => {
54
+ return item && typeof item === 'object' && item.type && (item.text || item.data);
55
+ });
56
+ if (validContent.length === 0) {
57
+ console.error(`āŒ Tool ${toolName} has invalid content items`);
58
+ return {
59
+ content: [{
60
+ type: 'text',
61
+ text: `āŒ Tool ${toolName} returned malformed content. Please report this issue.`
62
+ }],
63
+ isError: true
64
+ };
65
+ }
66
+ return {
67
+ content: validContent,
68
+ isError: response.isError
69
+ };
70
+ }
71
+ // If response is a string, wrap it
72
+ if (typeof response === 'string') {
73
+ return {
74
+ content: [{
75
+ type: 'text',
76
+ text: response
77
+ }]
78
+ };
79
+ }
80
+ // If response is in unexpected format
81
+ console.error(`āŒ Tool ${toolName} returned unexpected format:`, typeof response);
82
+ return {
83
+ content: [{
84
+ type: 'text',
85
+ text: `āŒ Tool ${toolName} returned unexpected format: ${typeof response}
86
+
87
+ Result: ${JSON.stringify(response, null, 2).substring(0, 500)}`
88
+ }],
89
+ isError: true
90
+ };
91
+ }
92
+ /**
93
+ * Wraps tool execution with response validation
94
+ */
95
+ export async function executeWithValidation(toolName, handler) {
96
+ try {
97
+ const result = await handler();
98
+ return validateMCPResponse(result, toolName);
99
+ }
100
+ catch (error) {
101
+ console.error(`āŒ Tool ${toolName} threw error:`, error);
102
+ const errorMessage = error instanceof Error ? error.message : String(error);
103
+ return {
104
+ content: [{
105
+ type: 'text',
106
+ text: `āŒ Tool execution failed: ${toolName}
107
+
108
+ Error: ${errorMessage}
109
+
110
+ ${error instanceof Error && error.stack ? `Stack:\n${error.stack.substring(0, 500)}` : ''}`
111
+ }],
112
+ isError: true
113
+ };
114
+ }
115
+ }
116
+ /**
117
+ * Creates a "no data" response with helpful message
118
+ */
119
+ export function createNoDataResponse(toolName, dataType, suggestions = []) {
120
+ let message = `ā„¹ļø ${toolName}: No ${dataType} found.`;
121
+ if (suggestions.length > 0) {
122
+ message += `\n\nšŸ’” Suggestions:\n${suggestions.map(s => ` • ${s}`).join('\n')}`;
123
+ }
124
+ return {
125
+ content: [{
126
+ type: 'text',
127
+ text: message
128
+ }]
129
+ };
130
+ }
131
+ /**
132
+ * Creates a success response with data
133
+ */
134
+ export function createSuccessResponse(toolName, message, data) {
135
+ let text = `āœ… ${message}`;
136
+ if (data) {
137
+ text += `\n\n${typeof data === 'string' ? data : JSON.stringify(data, null, 2)}`;
138
+ }
139
+ return {
140
+ content: [{
141
+ type: 'text',
142
+ text
143
+ }]
144
+ };
145
+ }