Skip to main content
Glama

firewalla-mcp-server

base.ts15.8 kB
/** * @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 type { FirewallaClient } from '../../firewalla/client.js'; import type { ToolResponseUnified } from '../../types.js'; import { createErrorResponse, ErrorType, } from '../../validation/error-handler.js'; import { validateAndSanitizeParameters, type SanitizationConfig, } 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'; /** * Base arguments interface for MCP tool execution * * Provides type-safe foundation for all tool arguments while maintaining flexibility * for tool-specific parameter extensions. Tools should extend this interface with * their specific argument requirements to ensure proper type checking. */ export interface BaseToolArgs { /** @description Optional limit for paginated results (recommended: 1-1000) */ limit?: number; /** @description Optional offset for paginated results */ offset?: number; /** @description Optional cursor for cursor-based pagination */ cursor?: string; /** @description Optional sorting field specification */ sort_by?: string; /** @description Optional sort order (ascending or descending) */ sort_order?: 'asc' | 'desc'; /** @description Optional grouping field for result aggregation */ group_by?: string; /** @description Optional flag to enable result aggregation */ aggregate?: boolean; /** @description Optional flag to force refresh and bypass cache */ force_refresh?: boolean; [key: string]: unknown; // Allow additional properties while maintaining base structure } /** * Common query parameters for search and filtering operations */ export interface QueryArgs { /** @description Query string for filtering results */ query?: string; /** @description Alternative query field name for compatibility */ queryBy?: string; /** @description Alternative sort field name for compatibility */ sortBy?: string; /** @description Alternative group field name for compatibility */ groupBy?: string; } /** * Time range parameters for temporal filtering */ export interface TimeRangeArgs { /** @description Start time for filtering (ISO string or Unix timestamp) */ start_time?: string | number; /** @description End time for filtering (ISO string or Unix timestamp) */ end_time?: string | number; /** @description Time range object with start and end */ time_range?: { start?: string | number; end?: string | number; }; } /** * Device-specific parameters */ export interface DeviceArgs { /** @description Specific device ID to filter by */ device_id?: string; /** @description Whether to include offline devices */ include_offline?: boolean; } /** * Geographic filtering parameters */ export interface GeographicArgs { /** @description Geographic filters object */ geographic_filters?: { countries?: string[]; continents?: string[]; regions?: string[]; cities?: string[]; asns?: string[]; hosting_providers?: string[]; exclude_cloud?: boolean; exclude_vpn?: boolean; min_risk_score?: number; }; } /** * Cross-reference and correlation parameters */ export interface CorrelationArgs { /** @description Primary query for correlation */ primary_query?: string; /** @description Secondary queries for correlation */ secondary_queries?: string[]; /** @description Field to correlate on */ correlation_field?: string; /** @description Correlation parameters object */ correlation_params?: { correlationFields?: string[]; correlationType?: 'AND' | 'OR'; temporalWindow?: { windowSize?: number; windowUnit?: string; }; networkScope?: { includeSubnets?: boolean; includePorts?: boolean; }; enableScoring?: boolean; enableFuzzyMatching?: boolean; minimumScore?: number; customWeights?: Record<string, number>; fuzzyConfig?: { enabled?: boolean; stringThreshold?: number; ipSubnetMatching?: boolean; numericTolerance?: number; geographicRadius?: number; }; }; } /** * Box/Group management parameters */ export interface BoxArgs { /** @description Group ID for filtering boxes */ group_id?: string; } /** * Comprehensive tool arguments interface that includes all common parameter patterns * used across the Firewalla MCP server tool handlers. * * This replaces the generic `any` type with specific, type-safe interfaces that * cover all the parameter patterns observed in the codebase while maintaining * backward compatibility. */ export interface ToolArgs extends BaseToolArgs, QueryArgs, TimeRangeArgs, DeviceArgs, GeographicArgs, CorrelationArgs, BoxArgs { // Additional tool-specific parameters can be added here // while maintaining type safety through the constituent interfaces } /** * Standardized response structure for MCP tool execution * * All tools must return responses in this format for consistent MCP protocol compliance. * The content array supports multiple response blocks with different types and formatting. */ export interface ToolResponse { /** @description Array of content blocks containing tool output */ content: Array<{ /** @description Content type (typically 'text' for JSON responses) */ type: string; /** @description The actual content text (usually JSON.stringify result) */ text: string; }>; /** @description Optional flag indicating if the response represents an error condition */ isError?: boolean; /** @description Additional metadata or context for the response */ [key: string]: unknown; } /** * Interface defining the contract for all MCP tool handlers * * Provides the foundation for implementing interactive tools that can be invoked * by Claude through the MCP protocol to access and manipulate Firewalla data. */ export interface ToolHandler { /** * Execute the tool with given arguments and return formatted response * * @param args - Tool-specific arguments provided by the MCP client * @param firewalla - Authenticated Firewalla client for API access * @returns Promise resolving to formatted tool response */ execute: ( args: ToolArgs, firewalla: FirewallaClient ) => Promise<ToolResponse>; /** @description Unique tool identifier used in MCP tool registration */ name: string; /** @description Human-readable description of tool functionality */ description: string; /** @description Tool category for organizational and filtering purposes */ category: 'security' | 'network' | 'device' | 'rule' | 'analytics' | 'search'; } /** * Configuration options for BaseToolHandler */ export interface BaseToolOptions { /** Whether to enable geographic enrichment for IP addresses */ enableGeoEnrichment?: boolean; /** Whether to enable field normalization to snake_case */ enableFieldNormalization?: boolean; /** Additional metadata to include in responses */ additionalMeta?: Record<string, any>; } /** * Generate a simple request ID for tracking */ function generateRequestId(): string { 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 abstract class BaseToolHandler implements ToolHandler { /** @description Tool identifier - must be implemented by concrete classes */ abstract name: string; /** @description Tool description - must be implemented by concrete classes */ abstract description: string; /** @description Tool category - must be implemented by concrete classes */ abstract category: | 'security' | 'network' | 'device' | 'rule' | 'analytics' | 'search'; /** @description Configuration options for this handler */ protected options: BaseToolOptions; /** * Constructor with default configuration */ constructor(options: BaseToolOptions = {}) { this.options = { enableGeoEnrichment: true, // Default to enabled for consistency enableFieldNormalization: true, // Default to enabled for consistency ...options, }; } /** * Execute the tool logic - must be implemented by concrete classes * * @param args - Tool arguments from MCP client * @param firewalla - Firewalla API client instance * @returns Promise resolving to tool response */ abstract execute( args: ToolArgs, firewalla: FirewallaClient ): Promise<ToolResponse>; /** * 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 */ protected createSuccessResponse(data: any): ToolResponse { 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 */ protected async createUnifiedResponse( data: any, options: { executionTimeMs?: number; requestId?: string; additionalMeta?: Record<string, any>; } = {} ): Promise<ToolResponse> { const startTime = Date.now(); let processedData = data; const meta: Record<string, any> = {}; // 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: ToolResponseUnified = { 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 */ protected async enrichGeoIfNeeded<T>( payload: T, ipFields: string[] = ['source_ip', 'destination_ip', 'device_ip', 'ip'] ): Promise<T> { if (!this.options.enableGeoEnrichment) { return payload; } try { const pipeline = getGlobalEnrichmentPipeline(geoCache); return (await pipeline.enrichObject(payload as any, ipFields)) as T; } 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 */ protected createErrorResponse( message: string, errorType: ErrorType = ErrorType.UNKNOWN_ERROR, details?: any, validationErrors?: string[] ): ToolResponse { 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 */ protected sanitizeParameters( rawArgs: unknown, config?: Partial<SanitizationConfig> ): { sanitizedArgs: ToolArgs } | { errorResponse: ToolResponse } { 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 */ protected async executeWithSanitization( rawArgs: unknown, firewalla: FirewallaClient, config?: Partial<SanitizationConfig> ): Promise<ToolResponse> { // 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); } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/amittell/firewalla-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server