firewalla-mcp-server
Version:
Model Context Protocol (MCP) server for Firewalla MSP API - Provides real-time network monitoring, security analysis, and firewall management through 28 specialized tools compatible with any MCP client
208 lines • 8.2 kB
JavaScript
/**
* @fileoverview Base types and interfaces for MCP tool handlers
*
* Provides foundational classes and interfaces for implementing MCP tools that
* interact with Firewalla firewall data. Includes standardized error handling,
* response formatting, and validation patterns for consistent tool behavior.
*
* The base infrastructure ensures all tools follow MCP protocol standards while
* providing consistent error reporting and response structure across the entire
* tool ecosystem.
*
* @version 1.0.0
* @author Alex Mittell <mittell@me.com> (https://github.com/amittell)
* @since 2025-06-21
*/
import { createErrorResponse, ErrorType, } from '../../validation/error-handler.js';
import { validateAndSanitizeParameters, } from '../../validation/parameter-sanitizer.js';
import { toSnakeCaseDeep } from '../../utils/field-normalizer.js';
import { enrichWithGeographicData, getGlobalEnrichmentPipeline, } from '../../utils/geographic-enrichment-pipeline.js';
import { geoCache } from '../../utils/geographic.js';
/**
* Generate a simple request ID for tracking
*/
function generateRequestId() {
return `req_${Date.now()}_${Math.random().toString(36).substring(2, 8)}`;
}
/**
* Base class for tool handlers with common validation and error handling
*
* Provides standardized implementation patterns for MCP tools including:
* - Unified response formatting with consistent metadata
* - Automatic geographic enrichment for IP addresses
* - Field normalization to snake_case
* - JSON serialization with proper error handling
* - Tool metadata structure validation
* - Common utility methods for response construction
*
* All concrete tool implementations should extend this class to ensure
* uniform behavior across the tool ecosystem.
*
* @abstract
* @implements {ToolHandler}
*/
export class BaseToolHandler {
/**
* Constructor with default configuration
*/
constructor(options = {}) {
this.options = {
enableGeoEnrichment: true, // Default to enabled for consistency
enableFieldNormalization: true, // Default to enabled for consistency
...options,
};
}
/**
* Create a legacy success response (DEPRECATED - Use createUnifiedResponse)
*
* @param data - The data to include in the response
* @returns Formatted success response compliant with MCP protocol
* @protected
* @deprecated Use createUnifiedResponse for new handlers
*/
createSuccessResponse(data) {
return {
content: [
{
type: 'text',
text: JSON.stringify(data, null, 2),
},
],
};
}
/**
* Create a unified success response with consistent formatting and enrichment
*
* @param data - The data to include in the response
* @param options - Additional options for response generation
* @returns Formatted success response with unified structure
* @protected
*/
async createUnifiedResponse(data, options = {}) {
const startTime = Date.now();
let processedData = data;
const meta = {};
// Apply geographic enrichment if enabled
if (this.options.enableGeoEnrichment) {
try {
processedData = await enrichWithGeographicData(processedData, geoCache);
meta.geo_enriched = true;
}
catch (error) {
// Geographic enrichment failure shouldn't break the response
meta.geo_enriched = false;
meta.geo_enrichment_error =
error instanceof Error ? error.message : 'Unknown error';
}
}
// Apply field normalization if enabled
if (this.options.enableFieldNormalization) {
try {
processedData = toSnakeCaseDeep(processedData);
meta.field_normalized = true;
}
catch (error) {
// Field normalization failure shouldn't break the response
meta.field_normalized = false;
meta.field_normalization_error =
error instanceof Error ? error.message : 'Unknown error';
}
}
// Build unified response
const unifiedResponse = {
success: true,
data: processedData,
meta: {
request_id: options.requestId || generateRequestId(),
execution_time_ms: options.executionTimeMs || Date.now() - startTime,
handler: this.name,
timestamp: new Date().toISOString(),
count: Array.isArray(processedData) ? processedData.length : undefined,
...meta,
...this.options.additionalMeta,
...options.additionalMeta,
},
};
// Convert to MCP ToolResponse format
return {
content: [
{
type: 'text',
text: JSON.stringify(unifiedResponse, null, 2),
},
],
};
}
/**
* Helper method for geographic enrichment that can be called by handlers
*
* @param payload - Data to enrich with geographic information
* @param ipFields - Array of IP field names to enrich (defaults to common fields)
* @returns Promise resolving to enriched data
* @protected
*/
async enrichGeoIfNeeded(payload, ipFields = ['source_ip', 'destination_ip', 'device_ip', 'ip']) {
if (!this.options.enableGeoEnrichment) {
return payload;
}
try {
const pipeline = getGlobalEnrichmentPipeline(geoCache);
return (await pipeline.enrichObject(payload, ipFields));
}
catch (_error) {
// Return original payload if enrichment fails
return payload;
}
}
/**
* Create a standardized error response with diagnostic information
*
* @param message - Human-readable error message
* @param errorType - Specific type of error (defaults to UNKNOWN_ERROR)
* @param details - Optional additional error context or debugging information
* @param validationErrors - Optional array of validation error messages
* @returns Formatted error response with isError flag set
* @protected
*/
createErrorResponse(message, errorType = ErrorType.UNKNOWN_ERROR, details, validationErrors) {
return createErrorResponse(this.name, message, errorType, details, validationErrors);
}
/**
* Sanitize and validate parameters early in the execution pipeline
*
* @param rawArgs - Raw arguments from MCP client
* @param config - Optional sanitization configuration
* @returns Sanitized arguments or error response
* @protected
*/
sanitizeParameters(rawArgs, config) {
const result = validateAndSanitizeParameters(rawArgs, this.name, config);
if ('errorResponse' in result) {
return { errorResponse: result.errorResponse };
}
return { sanitizedArgs: result.sanitizedArgs };
}
/**
* Execute tool with automatic parameter sanitization
*
* This is a convenience method that automatically sanitizes parameters
* before calling the tool's main execution logic. Tools can override
* this to customize sanitization behavior.
*
* @param rawArgs - Raw arguments from MCP client
* @param firewalla - Firewalla API client instance
* @param config - Optional sanitization configuration
* @returns Promise resolving to tool response
* @protected
*/
async executeWithSanitization(rawArgs, firewalla, config) {
// Early parameter sanitization
const sanitizationResult = this.sanitizeParameters(rawArgs, config);
if ('errorResponse' in sanitizationResult) {
return sanitizationResult.errorResponse;
}
// Call the tool's execute method with sanitized parameters
return this.execute(sanitizationResult.sanitizedArgs, firewalla);
}
}
//# sourceMappingURL=base.js.map