get_alarm_trends
Analyze historical alarm trends to identify patterns in daily security alerts from Firewalla firewall systems.
Instructions
Get historical alarm trend data (alarms generated per day)
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| group | No | Get trends for a specific box group |
Implementation Reference
- src/tools/handlers/analytics.ts:976-1134 (handler)The GetAlarmTrendsHandler class implements the core logic for the get_alarm_trends tool. It validates the optional 'period' parameter (1h, 24h, 7d, 30d), fetches alarm trends from Firewalla API via firewalla.getAlarmTrends(), processes the data with safe access patterns, normalizes timestamps, calculates summary statistics (total alarms, average, peak, frequency), and returns a unified response with execution metadata.export class GetAlarmTrendsHandler extends BaseToolHandler { name = 'get_alarm_trends'; description = 'Get historical alarm data trends over time with configurable periods. Optional period parameter. Data cached for 1 hour for performance.'; category = 'analytics' as const; constructor() { super({ enableGeoEnrichment: false, // No IP fields in alarm trends enableFieldNormalization: true, additionalMeta: { data_source: 'alarm_trends', entity_type: 'historical_alarm_data', supports_geographic_enrichment: false, supports_field_normalization: true, standardization_version: '2.0.0', }, }); } async execute( _args: ToolArgs, firewalla: FirewallaClient ): Promise<ToolResponse> { try { const periodValidation = ParameterValidator.validateEnum( _args?.period, 'period', ['1h', '24h', '7d', '30d'], false, '24h' ); if (!periodValidation.isValid) { return this.createErrorResponse( 'Parameter validation failed', ErrorType.VALIDATION_ERROR, undefined, periodValidation.errors ); } const period = periodValidation.sanitizedValue!; const trends = await withToolTimeout( async () => firewalla.getAlarmTrends(period as '1h' | '24h' | '7d' | '30d'), this.name ); // Defensive programming: validate trends response structure if ( !trends || !SafeAccess.getNestedValue(trends, 'results') || !Array.isArray(trends.results) ) { return this.createSuccessResponse({ period, data_points: 0, trends: [], summary: { total_alarms: 0, avg_alarms_per_interval: 0, peak_alarm_count: 0, intervals_with_alarms: 0, alarm_frequency: 0, }, error: 'Invalid alarm trends data received', }); } // Validate individual trend entries const validTrends = SafeAccess.safeArrayFilter( trends.results, (trend: any) => trend && typeof SafeAccess.getNestedValue(trend, 'ts') === 'number' && typeof SafeAccess.getNestedValue(trend, 'value') === 'number' && (SafeAccess.getNestedValue(trend, 'ts', 0) as number) > 0 && (SafeAccess.getNestedValue(trend, 'value', 0) as number) >= 0 ); const startTime = Date.now(); const unifiedResponseData = { period, data_points: validTrends.length, trends: SafeAccess.safeArrayMap(validTrends, (trend: any) => ({ timestamp: SafeAccess.getNestedValue(trend, 'ts', 0), timestamp_iso: unixToISOString( SafeAccess.getNestedValue(trend, 'ts', 0) as number ), alarm_count: SafeAccess.getNestedValue(trend, 'value', 0), })), summary: { total_alarms: validTrends.reduce( (sum: number, t: any) => sum + (SafeAccess.getNestedValue(t, 'value', 0) as number), 0 ), avg_alarms_per_interval: validTrends.length > 0 ? Math.round( (validTrends.reduce( (sum: number, t: any) => sum + (SafeAccess.getNestedValue(t, 'value', 0) as number), 0 ) / validTrends.length) * 100 ) / 100 : 0, // Performance Buffer Strategy: Same defensive slicing as flow trends // to prevent call stack overflow with large alarm trend datasets peak_alarm_count: validTrends.length > 0 ? Math.max( ...validTrends .slice(0, 1000) // Defensive limit to prevent call stack overflow .map( (t: any) => SafeAccess.getNestedValue(t, 'value', 0) as number ) ) : 0, intervals_with_alarms: SafeAccess.safeArrayFilter( validTrends, (t: any) => (SafeAccess.getNestedValue(t, 'value', 0) as number) > 0 ).length, alarm_frequency: validTrends.length > 0 ? Math.round( (SafeAccess.safeArrayFilter( validTrends, (t: any) => (SafeAccess.getNestedValue(t, 'value', 0) as number) > 0 ).length / validTrends.length) * 100 ) : 0, }, }; const executionTime = Date.now() - startTime; return this.createUnifiedResponse(unifiedResponseData, { executionTimeMs: executionTime, }); } catch (error: unknown) { const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred'; return this.createErrorResponse( `Failed to get alarm trends: ${errorMessage}`, ErrorType.API_ERROR ); } } }
- src/tools/registry.ts:167-167 (registration)Registration of the GetAlarmTrendsHandler in the ToolRegistry constructor, adding the tool to the central registry used by the MCP server.this.register(new GetAlarmTrendsHandler());
- src/server.ts:659-672 (schema)MCP tool schema definition for get_alarm_trends in the server's listTools response handler, specifying the tool name, description, and input schema with optional 'group' parameter.name: 'get_alarm_trends', description: 'Get historical alarm trend data (alarms generated per day)', inputSchema: { type: 'object', properties: { group: { type: 'string', description: 'Get trends for a specific box group', }, }, required: [], }, },
- src/tools/registry.ts:64-66 (registration)Import statement for GetAlarmTrendsHandler from the analytics handlers module, prerequisite for registration.GetAlarmTrendsHandler, GetRuleTrendsHandler, } from './handlers/analytics.js';