Skip to main content
Glama
amittell

firewalla-mcp-server

get_offline_devices

Retrieve offline network devices from Firewalla firewall to monitor connectivity status and identify disconnected endpoints.

Instructions

Get all offline devices (convenience wrapper around get_device_status)

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
limitNoMaximum number of offline devices to return
sort_by_last_seenNoSort devices by last seen time (default: true)
boxNoFilter devices under a specific Firewalla box

Implementation Reference

  • The main handler class for the 'get_offline_devices' tool. It fetches device status from Firewalla API, filters offline devices, applies sorting and limiting, normalizes data, enriches with geo info, and standardizes the response.
    export class GetOfflineDevicesHandler extends BaseToolHandler { name = 'get_offline_devices'; description = 'Get all offline devices with last seen timestamps and detailed device information. Requires limit parameter. Data cached for 2 minutes for performance.'; category = 'network' as const; constructor() { super({ enableGeoEnrichment: true, enableFieldNormalization: true, additionalMeta: { data_source: 'devices', entity_type: 'offline_devices', supports_geographic_enrichment: true, supports_field_normalization: true, standardization_version: '2.0.0', }, }); } async execute( args: ToolArgs, firewalla: FirewallaClient ): Promise<ToolResponse> { try { // Parameter validation with standardized limits const limitValidation = ParameterValidator.validateNumber( args?.limit, 'limit', { required: false, defaultValue: 100, ...getLimitValidationConfig(this.name), } ); const sortValidation = ParameterValidator.validateBoolean( args?.sort_by_last_seen, 'sort_by_last_seen', true ); const validationResult = ParameterValidator.combineValidationResults([ limitValidation, sortValidation, ]); if (!validationResult.isValid) { return this.createErrorResponse( 'Parameter validation failed', ErrorType.VALIDATION_ERROR, undefined, validationResult.errors ); } const limit = limitValidation.sanitizedValue! as number; const sortByLastSeen = sortValidation.sanitizedValue ?? true; // Buffer Strategy: Fetch extra devices to account for post-processing filtering // // Problem: When filtering for offline devices, we don't know how many devices // are offline until after fetching. If we only fetch the requested limit, // we might get fewer results than requested after filtering. // // Solution: Use a "buffer multiplier" strategy where we fetch 3x the requested // limit to increase the probability of having enough offline devices after // filtering. This trades some API overhead for more consistent result counts. // // The multiplier of 3 is empirically chosen based on typical online/offline // ratios in network environments (usually 60-80% devices are online). const fetchLimit = Math.min(limit * 3, 1000); // 3x buffer with 1000 cap for API limits const allDevicesResponse = await withToolTimeout( async () => firewalla.getDeviceStatus(undefined, undefined, fetchLimit), this.name ); // Normalize device data for consistency first const deviceResults = SafeAccess.safeArrayAccess( allDevicesResponse.results, (arr: any[]) => arr, [] ) as any[]; const normalizedDevices = batchNormalize(deviceResults, { name: (v: any) => sanitizeFieldValue(v, 'Unknown Device').value, ip: (v: any) => sanitizeFieldValue(v, 'unknown').value, macVendor: (v: any) => sanitizeFieldValue(v, 'unknown').value, network: (v: any) => (v ? normalizeUnknownFields(v) : null), group: (v: any) => (v ? normalizeUnknownFields(v) : null), online: (v: any) => Boolean(v), // Ensure consistent boolean handling }); // Filter to only offline devices with consistent boolean checking let offlineDevices = SafeAccess.safeArrayFilter( normalizedDevices, (device: any) => device.online === false ); // Sort by last seen timestamp if requested if (sortByLastSeen) { offlineDevices = offlineDevices.sort((a, b) => { const aTime = Number(SafeAccess.getNestedValue(a, 'lastSeen', 0)); const bTime = Number(SafeAccess.getNestedValue(b, 'lastSeen', 0)); return bTime - aTime; // Most recent first }); } // Apply the requested limit const limitedOfflineDevices = offlineDevices.slice(0, limit); const responseStartTime = Date.now(); // Process device data const deviceData = SafeAccess.safeArrayMap( limitedOfflineDevices, (device: any) => ({ id: SafeAccess.getNestedValue(device, 'id', 'unknown'), gid: SafeAccess.getNestedValue(device, 'gid', 'unknown'), name: device.name, // Already normalized ip: device.ip, // Already normalized macVendor: device.macVendor, // Already normalized online: device.online, // Already normalized to false for offline devices lastSeen: SafeAccess.getNestedValue(device, 'lastSeen', 0), lastSeenFormatted: safeUnixToISOString( SafeAccess.getNestedValue(device, 'lastSeen', 0) as number, 'Never' ), ipReserved: SafeAccess.getNestedValue(device, 'ipReserved', false), network: device.network, // Already normalized group: device.group, // Already normalized totalDownload: sanitizeByteCount( SafeAccess.getNestedValue(device, 'totalDownload', 0) ), totalUpload: sanitizeByteCount( SafeAccess.getNestedValue(device, 'totalUpload', 0) ), }) ); // Apply geographic enrichment for IP addresses const enrichedDeviceData = await this.enrichGeoIfNeeded(deviceData, [ 'ip', ]); const unifiedResponseData = { total_offline_devices: offlineDevices.length, limit_applied: limit, returned_count: limitedOfflineDevices.length, devices: enrichedDeviceData, }; const executionTime = Date.now() - responseStartTime; return this.createUnifiedResponse(unifiedResponseData, { executionTimeMs: executionTime, }); } catch (error: unknown) { // Handle timeout errors specifically if (error instanceof TimeoutError) { return createTimeoutErrorResponse( this.name, error.duration, 10000 // Default timeout from timeout-manager ); } const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred'; return this.createErrorResponse( `Failed to get offline devices: ${errorMessage}`, ErrorType.API_ERROR, { originalError: errorMessage } ); } } }
  • The JSON schema definition for the tool's input parameters, registered with the MCP server.
    name: 'get_offline_devices', description: 'Get all offline devices (convenience wrapper around get_device_status)', inputSchema: { type: 'object', properties: { limit: { type: 'number', description: 'Maximum number of offline devices to return', minimum: 1, maximum: 500, default: 100, }, sort_by_last_seen: { type: 'boolean', description: 'Sort devices by last seen time (default: true)', default: true, }, box: { type: 'string', description: 'Filter devices under a specific Firewalla box', }, }, required: [], },
  • Registers the GetOfflineDevicesHandler instance in the central ToolRegistry during initialization.
    this.register(new GetOfflineDevicesHandler()); // wrapper around get_device_status
  • Defines the rate limiting configuration for the tool via getToolLimit function.
    get_offline_devices: STANDARD_LIMITS.OFFLINE_DEVICES,

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