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
532 lines • 18.8 kB
TypeScript
/**
* @fileoverview Firewalla API Client for MSP Integration
*
* Provides comprehensive access to Firewalla MSP APIs with enterprise-grade features:
* - **Authentication**: Token-based MSP API authentication with error handling
* - **Caching**: Intelligent response caching with configurable TTL
* - **Rate Limiting**: Built-in protection against API rate limits
* - **Error Handling**: Comprehensive error mapping and recovery strategies
* - **Optimization**: Automatic response optimization for token efficiency
* - **Monitoring**: Request/response logging and performance tracking
*
* The client supports all major Firewalla data types including alarms, flows,
* devices, rules, bandwidth analytics, and advanced search capabilities with
* cross-reference correlation and trend analysis.
*
* @version 1.0.0
* @author Alex Mittell <mittell@me.com> (https://github.com/amittell)
* @since 2025-06-21
*/
import { FirewallaConfig, Alarm, Flow, Device, BandwidthUsage, NetworkRule, TargetList, Box, SearchResult, SearchQuery, SearchOptions, CrossReferenceResult, Trend, SimpleStats, Statistics } from '../types.js';
import { type GeographicCacheStats } from '../utils/geographic.js';
/**
* Firewalla API Client for MSP Integration
*
* Main client class providing authenticated access to Firewalla MSP APIs.
* Handles authentication, caching, rate limiting, error handling, and response
* optimization for efficient integration with Claude through the MCP protocol.
*
* Features:
* - Automatic token-based authentication with the MSP API
* - Intelligent caching with configurable TTL policies
* - Built-in rate limiting and retry mechanisms
* - Comprehensive error handling with meaningful error messages
* - Response optimization for MCP protocol constraints
* - Request/response logging for debugging and monitoring
*
* @example
* ```typescript
* const config = getConfig();
* const client = new FirewallaClient(config);
*
* // Get recent alarms
* const alarms = await client.getActiveAlarms({ limit: 50 });
*
* // Search for high-severity flows
* const flows = await client.searchFlows({
* query: 'severity:high AND bytes:>1000000',
* limit: 100
* });
* ```
*
* @class
* @public
*/
export declare class FirewallaClient {
private config;
/** @private Axios instance configured for Firewalla MSP API access */
private api;
/** @private In-memory cache for API responses with TTL management */
private cache;
/** @private Geographic cache for IP geolocation lookups */
private geoCache;
/**
* Creates a new Firewalla API client instance
*
* @param config - Configuration object containing MSP credentials and settings
* @throws {Error} If configuration is invalid or authentication fails
*/
constructor(config: FirewallaConfig);
/**
* Sets up Axios request and response interceptors for logging and error handling
*
* Configures interceptors to:
* - Log all API requests and responses for debugging
* - Transform HTTP error codes into meaningful error messages
* - Handle authentication and authorization failures
* - Provide specific guidance for common error scenarios
*
* @private
* @returns {void}
*/
private setupInterceptors;
/**
* Generates a unique cache key for API requests with enhanced collision prevention
*
* Creates a cache key that includes the box ID, endpoint, method, and sorted parameters
* to ensure uniqueness across different boxes and API calls.
*
* @param endpoint - API endpoint path
* @param params - Optional request parameters
* @param method - HTTP method (default: 'GET')
* @returns Unique cache key string with collision prevention
* @private
*/
private getCacheKey;
/**
* Retrieves data from cache if available and not expired
*
* @template T - The expected return type
* @param key - Cache key to look up
* @returns Cached data if available and valid, otherwise null
* @private
*/
private getFromCache;
private setCache;
private sanitizeInput;
/**
* Filter parameters for GET requests to /v2/* endpoints to only include allowed scalar fields
* Fixes issue where complex objects get serialized as [object Object] causing "Bad Request" errors
*/
private filterParametersForDataEndpoints;
private request;
/**
* Retrieves active security alarms from the Firewalla system
*
* Fetches current security alerts, alarms, and notifications with support for
* advanced filtering, grouping, and pagination. Results are automatically
* optimized for token efficiency while preserving essential security context.
*
* @param query - Optional search query for filtering alarms
* @param groupBy - Optional field to group results by (e.g., 'type', 'box')
* @param sortBy - Sort order specification (default: 'timestamp:desc')
* @param limit - Maximum number of results to return (required for pagination)
* @param cursor - Pagination cursor from previous response
* @returns Promise resolving to paginated alarm results with metadata
*
* @example
* ```typescript
* // Get recent high-severity alarms
* const highSeverityAlarms = await client.getActiveAlarms(
* 'severity:high',
* undefined,
* 'timestamp:desc',
* 50
* );
*
* // Get alarms grouped by type
* const groupedAlarms = await client.getActiveAlarms(
* undefined,
* 'type',
* 'timestamp:desc',
* 100
* );
* ```
*
* @public
* @optimizeResponse('alarms') - Automatically optimizes response for token efficiency
*/
getActiveAlarms(query?: string, groupBy?: string, sortBy?: string, limit?: number, cursor?: string, force_refresh?: boolean): Promise<{
count: number;
results: Alarm[];
next_cursor?: string;
}>;
getFlowData(query?: string, groupBy?: string, sortBy?: string, limit?: number, cursor?: string): Promise<{
count: number;
results: Flow[];
next_cursor?: string;
}>;
getDeviceStatus(deviceId?: string, includeOffline?: boolean, limit?: number, cursor?: string): Promise<{
count: number;
results: Device[];
next_cursor?: string;
total_count: number;
has_more: boolean;
}>;
getOfflineDevices(sortByLastSeen?: boolean): Promise<{
count: number;
results: Device[];
next_cursor?: string;
}>;
private transformDevice;
/**
* Get top bandwidth consuming devices on the network
*
* @param period - Time period for analysis ('1h', '24h', '7d', '30d')
* @param top - Maximum number of devices to return (default: 10, max: 500)
* @returns Promise resolving to bandwidth usage data with device details
* @throws {Error} If period is invalid or API request fails
* @example
* ```typescript
* const usage = await client.getBandwidthUsage('24h', 50);
* usage.results.forEach(device => {
* console.log(`${device.name}: ${device.total_bytes} bytes`);
* });
* ```
*/
getBandwidthUsage(period: string, top?: number): Promise<{
count: number;
results: BandwidthUsage[];
next_cursor?: string;
}>;
getNetworkRules(query?: string, limit?: number): Promise<{
count: number;
results: NetworkRule[];
next_cursor?: string;
}>;
getTargetLists(listType?: string, limit?: number): Promise<{
count: number;
results: TargetList[];
next_cursor?: string;
}>;
/**
* Get a specific target list by ID
*/
getSpecificTargetList(id: string): Promise<TargetList>;
/**
* Create a new target list
*/
createTargetList(targetListData: {
name: string;
owner: string;
targets: string[];
category?: string;
notes?: string;
}): Promise<TargetList>;
/**
* Update an existing target list
*/
updateTargetList(id: string, updateData: {
name?: string;
targets?: string[];
category?: string;
notes?: string;
}): Promise<TargetList>;
/**
* Delete a target list
*/
deleteTargetList(id: string): Promise<{
success: boolean;
message: string;
}>;
getFirewallSummary(): Promise<{
status: string;
uptime: number;
cpu_usage: number;
memory_usage: number;
active_connections: number;
blocked_attempts: number;
last_updated: string;
}>;
getSecurityMetrics(): Promise<{
total_alarms: number;
active_alarms: number;
blocked_connections: number;
suspicious_activities: number;
threat_level: 'low' | 'medium' | 'high' | 'critical';
last_threat_detected: string;
}>;
getNetworkTopology(): Promise<{
subnets: Array<{
id: string;
name: string;
cidr: string;
device_count: number;
}>;
connections: Array<{
source: string;
destination: string;
type: string;
bandwidth: number;
}>;
}>;
getRecentThreats(hours?: number): Promise<Array<{
timestamp: string;
type: string;
source_ip: string;
destination_ip: string;
action_taken: string;
severity: string;
}>>;
getBoxes(groupId?: string): Promise<{
count: number;
results: Box[];
next_cursor?: string;
}>;
getSpecificAlarm(alarmId: string, gid?: string): Promise<{
count: number;
results: Alarm[];
next_cursor?: string;
}>;
deleteAlarm(alarmId: string, gid?: string): Promise<any>;
getSimpleStatistics(): Promise<{
count: number;
results: SimpleStats[];
next_cursor?: string;
}>;
getStatisticsByRegion(): Promise<{
count: number;
results: Statistics[];
next_cursor?: string;
}>;
getFlowTrends(period?: '1h' | '24h' | '7d' | '30d', interval?: number): Promise<{
count: number;
results: Trend[];
next_cursor?: string;
}>;
getAlarmTrends(period?: '1h' | '24h' | '7d' | '30d'): Promise<{
count: number;
results: Trend[];
next_cursor?: string;
}>;
getRuleTrends(period?: '1h' | '24h' | '7d' | '30d'): Promise<{
count: number;
results: Trend[];
next_cursor?: string;
}>;
getStatisticsByBox(): Promise<{
count: number;
results: Statistics[];
next_cursor?: string;
}>;
clearCache(): void;
getCacheStats(): {
size: number;
keys: string[];
};
/**
* Get geographic cache statistics
*/
getGeographicCacheStats(): GeographicCacheStats;
/**
* Clear geographic cache
*/
clearGeographicCache(): void;
/**
* Get geographic data for an IP address with caching
* @param ip - IP address to geolocate
* @returns GeographicData object or null if lookup fails or IP is private
*/
private getGeographicData;
/**
* Set field value in object using dot notation
* @param obj - Object to modify
* @param fieldPath - Dot notation path (e.g., 'destination.geo')
* @param value - Value to set
*/
private setFieldValue;
/**
* Generic method to enrich object with geographic data based on IP paths
* @param obj - Object to enrich
* @param ipPaths - Array of dot notation paths to IP fields (optional, defaults to common flow/alarm paths)
* @returns Enriched object with geographic data
*/
private enrichWithGeographicData;
/**
* Backward compatibility method for alarm enrichment
* @param alarm - Alarm object to enrich
* @returns Enriched alarm with geographic data
*/
enrichAlarmWithGeographicData(alarm: any): any;
/**
* Advanced search for network flows with complex query syntax
* Supports: severity:high AND source_ip:192.168.* NOT resolved:true
*/
searchFlows(searchQuery: SearchQuery, options?: SearchOptions): Promise<SearchResult<Flow>>;
/**
* Advanced search for security alarms with severity, time, and IP filters
*/
searchAlarms(searchQuery: SearchQuery, options?: SearchOptions): Promise<SearchResult<Alarm>>;
/**
* Advanced search for firewall rules with target, action, and status filters
*/
searchRules(searchQuery: SearchQuery, options?: SearchOptions): Promise<SearchResult<NetworkRule>>;
/**
* Advanced search for network devices with network, status, and usage filters
*/
searchDevices(searchQuery: SearchQuery, options?: SearchOptions): Promise<SearchResult<Device>>;
/**
* Advanced search for target lists with category and ownership filters
*/
searchTargetLists(searchQuery: SearchQuery, options?: SearchOptions): Promise<SearchResult<TargetList>>;
/**
* Multi-entity searches with correlation across different data types
* Enhanced with proper entity type handling
*/
searchCrossReference(primaryQuery: SearchQuery, secondaryQueries: Record<string, SearchQuery>, correlationField: string, options?: SearchOptions, primaryEntityType?: 'flows' | 'alarms' | 'rules' | 'devices'): Promise<CrossReferenceResult>;
/**
* Execute search based on entity type
*/
private executeSearchByEntityType;
/**
* Infer entity type from query name (fallback for backward compatibility)
*/
private inferEntityTypeFromName;
/**
* Get overview statistics and counts of network rules by category
*/
getNetworkRulesSummary(activeOnly?: boolean, ruleType?: string): Promise<{
count: number;
results: any[];
next_cursor?: string;
}>;
/**
* Get rules with highest hit counts for traffic analysis
*/
getMostActiveRules(limit?: number, minHits?: number, ruleType?: string): Promise<{
count: number;
results: NetworkRule[];
next_cursor?: string;
}>;
/**
* Get recently created or modified firewall rules
*/
getRecentRules(hours?: number, includeModified?: boolean, limit?: number, ruleType?: string): Promise<{
count: number;
results: NetworkRule[];
next_cursor?: string;
}>;
/**
* Temporarily disable a specific firewall rule for a specified duration
*
* @param ruleId - The unique identifier of the rule to pause
* @param durationMinutes - Duration in minutes to pause the rule (default: 60, max: 1440)
* @returns Promise resolving to operation result with success status and message
* @throws {Error} If rule ID is invalid or API request fails
* @example
* ```typescript
* const result = await client.pauseRule('rule-123', 30);
* console.log(result.message); // "Rule paused successfully"
* ```
*/
pauseRule(ruleId: string, durationMinutes?: number): Promise<{
success: boolean;
message: string;
}>;
/**
* Resume a previously paused firewall rule, restoring it to active state
*
* @param ruleId - The unique identifier of the rule to resume
* @returns Promise resolving to operation result with success status and message
* @throws {Error} If rule ID is invalid or rule is not paused
* @example
* ```typescript
* const result = await client.resumeRule('rule-123');
* console.log(result.message); // "Rule resumed successfully"
* ```
*/
resumeRule(ruleId: string): Promise<{
success: boolean;
message: string;
}>;
/**
* Helper method to build OR queries for array-based geographic filters
*
* @param fieldName - The field name for the query (e.g., 'country', 'region')
* @param values - Array of values to include in the OR query
* @returns Query string or null if values array is empty
* @private
*/
private buildArrayFilterQuery;
/**
* Helper method to add box.id qualifier to search queries
*
* @param query - Existing query string (optional)
* @returns Query string with box.id filter added, or just box.id filter if no query
* @private
*/
private addBoxFilter;
/**
* Build geographic query string from filters for Firewalla API
*
* Converts geographic filter objects into API-compatible query syntax.
* Supports countries, continents, regions, cities, ASNs, hosting providers,
* and boolean exclusion filters.
*
* @param filters - Geographic filter configuration
* @returns Query string compatible with Firewalla API
*/
buildGeoQuery(filters: {
countries?: string[];
continents?: string[];
regions?: string[];
cities?: string[];
asns?: string[];
hosting_providers?: string[];
exclude_cloud?: boolean;
exclude_vpn?: boolean;
min_risk_score?: number;
high_risk_countries?: boolean;
exclude_known_providers?: boolean;
threat_analysis?: boolean;
}): string;
/**
* Extract field value from object using dot notation
*/
private extractFieldValue;
/**
* Extract and validate string values with optional allowed values
*/
private extractValidString;
/**
* Public method for making raw API calls
* Used by management tools for bulk operations
*/
makeApiCall(method: 'get' | 'post' | 'patch' | 'delete', endpoint: string, data?: any): Promise<any>;
/**
* Get flow insights with category-based analysis
* This provides category breakdowns and bandwidth analysis for networks with high flow volumes
*/
getFlowInsights(period?: '1h' | '24h' | '7d' | '30d', options?: {
categories?: string[];
includeBlocked?: boolean;
}): Promise<{
period: string;
categoryBreakdown: Array<{
category: string;
count: number;
bytes: number;
topDomains: Array<{
domain: string;
count: number;
bytes: number;
}>;
}>;
topDevices: Array<{
device: string;
totalBytes: number;
categories: Array<{
category: string;
bytes: number;
}>;
}>;
blockedSummary?: {
totalBlocked: number;
byCategory: Array<{
category: string;
count: number;
}>;
};
}>;
}
//# sourceMappingURL=client.d.ts.map