Skip to main content
Glama

Indigo MCP Server Plugin

by mlamoure
tool_wrappers.py23.5 kB
""" Tool wrapper methods for MCP server. Provides wrapper methods that connect MCP tool calls to their implementations. Each wrapper handles parameter validation, error handling, and JSON serialization. """ from typing import Any, Dict, List, Optional import logging from .common.json_encoder import safe_json_dumps from .common.indigo_device_types import DeviceTypeResolver, IndigoDeviceType, IndigoEntityType class ToolWrappers: """Collection of wrapper methods for MCP tools.""" def __init__( self, search_handler, get_devices_by_type_handler, device_control_handler, rgb_control_handler, thermostat_control_handler, variable_control_handler, action_control_handler, historical_analysis_handler, list_handlers, log_query_handler, plugin_control_handler, data_provider, logger: Optional[logging.Logger] = None ): """ Initialize tool wrappers with handler dependencies. Args: search_handler: Search entities handler get_devices_by_type_handler: Get devices by type handler device_control_handler: Device control handler rgb_control_handler: RGB control handler thermostat_control_handler: Thermostat control handler variable_control_handler: Variable control handler action_control_handler: Action control handler historical_analysis_handler: Historical analysis handler list_handlers: List handlers instance log_query_handler: Log query handler plugin_control_handler: Plugin control handler data_provider: Data provider for direct entity access logger: Optional logger instance """ self.search_handler = search_handler self.get_devices_by_type_handler = get_devices_by_type_handler self.device_control_handler = device_control_handler self.rgb_control_handler = rgb_control_handler self.thermostat_control_handler = thermostat_control_handler self.variable_control_handler = variable_control_handler self.action_control_handler = action_control_handler self.historical_analysis_handler = historical_analysis_handler self.list_handlers = list_handlers self.log_query_handler = log_query_handler self.plugin_control_handler = plugin_control_handler self.data_provider = data_provider self.logger = logger or logging.getLogger(__name__) # Tool wrapper methods def tool_search_entities( self, query: str, device_types: List[str] = None, entity_types: List[str] = None, state_filter: Dict = None, limit: int = 50, offset: int = 0 ) -> str: """Search entities tool implementation with pagination.""" try: # Validate device types if device_types: resolved_types, invalid_device_types = DeviceTypeResolver.resolve_device_types(device_types) if invalid_device_types: # Generate helpful error message with suggestions error_parts = [f"Invalid device types: {invalid_device_types}"] error_parts.append(f"Valid types: {IndigoDeviceType.get_all_types()}") # Add suggestions for each invalid type for invalid_type in invalid_device_types: suggestions = DeviceTypeResolver.get_suggestions_for_invalid_type(invalid_type) if suggestions: error_parts.append(f"Did you mean: {', '.join(suggestions)}") return safe_json_dumps({ "error": " | ".join(error_parts), "query": query }) # Use resolved types for the search device_types = resolved_types # Validate entity types if entity_types: invalid_entity_types = [ et for et in entity_types if not IndigoEntityType.is_valid_type(et) ] if invalid_entity_types: return safe_json_dumps({ "error": f"Invalid entity types: {invalid_entity_types}", "query": query }) self.logger.info( f"[search_entities]: query: '{query}', " f"device_types: {device_types}, " f"entity_types: {entity_types}, " f"state_filter: {state_filter}, " f"limit: {limit}, offset: {offset}" ) results = self.search_handler.search( query=query, device_types=device_types, entity_types=entity_types, state_filter=state_filter, limit=limit, offset=offset ) return safe_json_dumps(results) except Exception as e: self.logger.error(f"[search_entities]: Error - {e}") return safe_json_dumps({"error": str(e), "query": query}) def tool_get_devices_by_type(self, device_type: str, limit: int = 50, offset: int = 0) -> str: """Get devices by type tool implementation with pagination.""" try: result = self.get_devices_by_type_handler.get_devices(device_type, limit=limit, offset=offset) return safe_json_dumps(result) except Exception as e: self.logger.error(f"Get devices by type error: {e}") return safe_json_dumps({"error": str(e), "device_type": device_type}) def tool_device_turn_on(self, device_id: int) -> str: """Turn on device tool implementation.""" try: result = self.device_control_handler.turn_on(device_id) return safe_json_dumps(result) except Exception as e: self.logger.error(f"Device turn on error: {e}") return safe_json_dumps({"error": str(e)}) def tool_device_turn_off(self, device_id: int) -> str: """Turn off device tool implementation.""" try: result = self.device_control_handler.turn_off(device_id) return safe_json_dumps(result) except Exception as e: self.logger.error(f"Device turn off error: {e}") return safe_json_dumps({"error": str(e)}) def tool_device_set_brightness(self, device_id: int, brightness: float) -> str: """Set device brightness tool implementation.""" try: result = self.device_control_handler.set_brightness(device_id, brightness) return safe_json_dumps(result) except Exception as e: self.logger.error(f"Device set brightness error: {e}") return safe_json_dumps({"error": str(e)}) def tool_device_set_rgb_color( self, device_id: int, red: int, green: int, blue: int, delay: int = 0 ) -> str: """Set RGB color using 0-255 values tool implementation.""" try: result = self.rgb_control_handler.set_rgb_color(device_id, red, green, blue, delay) return safe_json_dumps(result) except Exception as e: self.logger.error(f"RGB color set error: {e}") return safe_json_dumps({"error": str(e)}) def tool_device_set_rgb_percent( self, device_id: int, red_percent: float, green_percent: float, blue_percent: float, delay: int = 0 ) -> str: """Set RGB color using 0-100 percentages tool implementation.""" try: result = self.rgb_control_handler.set_rgb_percent( device_id, red_percent, green_percent, blue_percent, delay ) return safe_json_dumps(result) except Exception as e: self.logger.error(f"RGB percent set error: {e}") return safe_json_dumps({"error": str(e)}) def tool_device_set_hex_color( self, device_id: int, hex_color: str, delay: int = 0 ) -> str: """Set RGB color using hex code tool implementation.""" try: result = self.rgb_control_handler.set_hex_color(device_id, hex_color, delay) return safe_json_dumps(result) except Exception as e: self.logger.error(f"Hex color set error: {e}") return safe_json_dumps({"error": str(e)}) def tool_device_set_named_color( self, device_id: int, color_name: str, delay: int = 0 ) -> str: """Set RGB color using named color tool implementation.""" try: result = self.rgb_control_handler.set_named_color(device_id, color_name, delay) return safe_json_dumps(result) except Exception as e: self.logger.error(f"Named color set error: {e}") return safe_json_dumps({"error": str(e)}) def tool_device_set_white_levels( self, device_id: int, white_level: Optional[float] = None, white_level2: Optional[float] = None, white_temperature: Optional[int] = None, delay: int = 0 ) -> str: """Set white channel levels tool implementation.""" try: result = self.rgb_control_handler.set_white_levels( device_id, white_level, white_level2, white_temperature, delay ) return safe_json_dumps(result) except Exception as e: self.logger.error(f"White levels set error: {e}") return safe_json_dumps({"error": str(e)}) def tool_thermostat_set_heat_setpoint(self, device_id: int, temperature: float) -> str: """Set thermostat heat setpoint tool implementation.""" try: result = self.thermostat_control_handler.set_heat_setpoint(device_id, temperature) return safe_json_dumps(result) except Exception as e: self.logger.error(f"Thermostat heat setpoint error: {e}") return safe_json_dumps({"error": str(e)}) def tool_thermostat_set_cool_setpoint(self, device_id: int, temperature: float) -> str: """Set thermostat cool setpoint tool implementation.""" try: result = self.thermostat_control_handler.set_cool_setpoint(device_id, temperature) return safe_json_dumps(result) except Exception as e: self.logger.error(f"Thermostat cool setpoint error: {e}") return safe_json_dumps({"error": str(e)}) def tool_thermostat_set_hvac_mode(self, device_id: int, mode: str) -> str: """Set thermostat HVAC mode tool implementation.""" try: result = self.thermostat_control_handler.set_hvac_mode(device_id, mode) return safe_json_dumps(result) except Exception as e: self.logger.error(f"Thermostat HVAC mode error: {e}") return safe_json_dumps({"error": str(e)}) def tool_thermostat_set_fan_mode(self, device_id: int, mode: str) -> str: """Set thermostat fan mode tool implementation.""" try: result = self.thermostat_control_handler.set_fan_mode(device_id, mode) return safe_json_dumps(result) except Exception as e: self.logger.error(f"Thermostat fan mode error: {e}") return safe_json_dumps({"error": str(e)}) def tool_variable_update(self, variable_id: int, value: str) -> str: """Update variable tool implementation.""" try: result = self.variable_control_handler.update(variable_id, value) return safe_json_dumps(result) except Exception as e: self.logger.error(f"Variable update error: {e}") return safe_json_dumps({"error": str(e)}) def tool_variable_create( self, name: str, value: str = "", folder_id: int = 0 ) -> str: """Create variable tool implementation.""" try: result = self.variable_control_handler.create(name, value, folder_id) return safe_json_dumps(result) except Exception as e: self.logger.error(f"Variable create error: {e}") return safe_json_dumps({"error": str(e)}) def tool_action_execute_group( self, action_group_id: int, delay: int = None ) -> str: """Execute action group tool implementation.""" try: result = self.action_control_handler.execute(action_group_id, delay) return safe_json_dumps(result) except Exception as e: self.logger.error(f"Action execute error: {e}") return safe_json_dumps({"error": str(e)}) def tool_analyze_historical_data( self, query: str, device_names: List[str], time_range_days: int = 30 ) -> str: """Analyze historical data tool implementation.""" try: result = self.historical_analysis_handler.analyze_historical_data( query, device_names, time_range_days ) return safe_json_dumps(result) except Exception as e: self.logger.error(f"Historical analysis error: {e}") return safe_json_dumps({"error": str(e)}) def tool_list_devices( self, state_filter: Dict = None, limit: int = 50, offset: int = 0 ) -> str: """List devices tool implementation with pagination.""" try: result = self.list_handlers.list_all_devices( state_filter=state_filter, limit=limit, offset=offset ) return safe_json_dumps(result) except Exception as e: self.logger.error(f"List devices error: {e}") return safe_json_dumps({"error": str(e)}) def tool_list_variables(self, limit: int = 50, offset: int = 0) -> str: """List variables tool implementation with pagination.""" try: result = self.list_handlers.list_all_variables(limit=limit, offset=offset) return safe_json_dumps(result) except Exception as e: self.logger.error(f"List variables error: {e}") return safe_json_dumps({"error": str(e)}) def tool_list_action_groups(self, limit: int = 50, offset: int = 0) -> str: """List action groups tool implementation with pagination.""" try: result = self.list_handlers.list_all_action_groups(limit=limit, offset=offset) return safe_json_dumps(result) except Exception as e: self.logger.error(f"List action groups error: {e}") return safe_json_dumps({"error": str(e)}) def tool_list_variable_folders(self) -> str: """List variable folders tool implementation.""" try: folders = self.list_handlers.list_variable_folders() return safe_json_dumps(folders) except Exception as e: self.logger.error(f"List variable folders error: {e}") return safe_json_dumps({"error": str(e)}) def tool_get_devices_by_state( self, state_conditions: Dict, device_types: List[str] = None, limit: int = 50, offset: int = 0 ) -> str: """Get devices by state tool implementation with pagination.""" try: # Validate device types if provided if device_types: resolved_types, invalid_types = DeviceTypeResolver.resolve_device_types(device_types) if invalid_types: # Generate helpful error message with suggestions error_parts = [f"Invalid device types: {invalid_types}"] error_parts.append(f"Valid types: {IndigoDeviceType.get_all_types()}") # Add suggestions for each invalid type for invalid_type in invalid_types: suggestions = DeviceTypeResolver.get_suggestions_for_invalid_type(invalid_type) if suggestions: error_parts.append(f"Did you mean: {', '.join(suggestions)}") return safe_json_dumps({ "error": " | ".join(error_parts) }) # Use resolved types for the query device_types = resolved_types result = self.list_handlers.get_devices_by_state( state_conditions=state_conditions, device_types=device_types, limit=limit, offset=offset ) return safe_json_dumps(result) except Exception as e: self.logger.error(f"Get devices by state error: {e}") return safe_json_dumps({"error": str(e)}) def tool_get_device_by_id(self, device_id: int) -> str: """Get device by ID tool implementation.""" try: device = self.data_provider.get_device(device_id) if device is None: return safe_json_dumps({ "error": f"Device {device_id} not found" }) return safe_json_dumps(device) except Exception as e: self.logger.error(f"Get device by ID error: {e}") return safe_json_dumps({"error": str(e)}) def tool_get_variable_by_id(self, variable_id: int) -> str: """Get variable by ID tool implementation.""" try: variable = self.data_provider.get_variable(variable_id) if variable is None: return safe_json_dumps({ "error": f"Variable {variable_id} not found" }) return safe_json_dumps(variable) except Exception as e: self.logger.error(f"Get variable by ID error: {e}") return safe_json_dumps({"error": str(e)}) def tool_get_action_group_by_id(self, action_group_id: int) -> str: """Get action group by ID tool implementation.""" try: action = self.data_provider.get_action_group(action_group_id) if action is None: return safe_json_dumps({ "error": f"Action group {action_group_id} not found" }) return safe_json_dumps(action) except Exception as e: self.logger.error(f"Get action group by ID error: {e}") return safe_json_dumps({"error": str(e)}) def tool_query_event_log( self, line_count: int = 20, show_timestamp: bool = True ) -> str: """Query event log tool implementation.""" try: result = self.log_query_handler.query( line_count=line_count, show_timestamp=show_timestamp ) return safe_json_dumps(result) except Exception as e: self.logger.error(f"Query event log error: {e}") return safe_json_dumps({"error": str(e)}) def tool_list_plugins(self, include_disabled: bool = False) -> str: """List plugins tool implementation.""" try: result = self.plugin_control_handler.list_plugins(include_disabled) return safe_json_dumps(result) except Exception as e: self.logger.error(f"List plugins error: {e}") return safe_json_dumps({"error": str(e)}) def tool_get_plugin_by_id(self, plugin_id: str) -> str: """Get plugin by ID tool implementation.""" try: result = self.plugin_control_handler.get_plugin_by_id(plugin_id) return safe_json_dumps(result) except Exception as e: self.logger.error(f"Get plugin by ID error: {e}") return safe_json_dumps({"error": str(e)}) def tool_restart_plugin(self, plugin_id: str) -> str: """Restart plugin tool implementation.""" try: result = self.plugin_control_handler.restart_plugin(plugin_id) return safe_json_dumps(result) except Exception as e: self.logger.error(f"Restart plugin error: {e}") return safe_json_dumps({"error": str(e)}) def tool_get_plugin_status(self, plugin_id: str) -> str: """Get plugin status tool implementation.""" try: result = self.plugin_control_handler.get_plugin_status(plugin_id) return safe_json_dumps(result) except Exception as e: self.logger.error(f"Get plugin status error: {e}") return safe_json_dumps({"error": str(e)}) # Resource wrapper methods def resource_list_devices(self) -> str: """List all devices resource.""" try: devices = self.list_handlers.list_all_devices() return safe_json_dumps(devices) except Exception as e: self.logger.error(f"Resource list devices error: {e}") return safe_json_dumps({"error": str(e)}) def resource_get_device(self, device_id: str) -> str: """Get specific device resource.""" try: device = self.data_provider.get_device(int(device_id)) if device is None: return safe_json_dumps({ "error": f"Device {device_id} not found" }) return safe_json_dumps(device) except Exception as e: self.logger.error(f"Resource get device error: {e}") return safe_json_dumps({"error": str(e)}) def resource_list_variables(self) -> str: """List all variables resource.""" try: variables = self.list_handlers.list_all_variables() return safe_json_dumps(variables) except Exception as e: self.logger.error(f"Resource list variables error: {e}") return safe_json_dumps({"error": str(e)}) def resource_get_variable(self, variable_id: str) -> str: """Get specific variable resource.""" try: variable = self.data_provider.get_variable(int(variable_id)) if variable is None: return safe_json_dumps({ "error": f"Variable {variable_id} not found" }) return safe_json_dumps(variable) except Exception as e: self.logger.error(f"Resource get variable error: {e}") return safe_json_dumps({"error": str(e)}) def resource_list_actions(self) -> str: """List all action groups resource.""" try: actions = self.list_handlers.list_all_action_groups() return safe_json_dumps(actions) except Exception as e: self.logger.error(f"Resource list actions error: {e}") return safe_json_dumps({"error": str(e)}) def resource_get_action(self, action_id: str) -> str: """Get specific action group resource.""" try: action = self.data_provider.get_action_group(int(action_id)) if action is None: return safe_json_dumps({ "error": f"Action group {action_id} not found" }) return safe_json_dumps(action) except Exception as e: self.logger.error(f"Resource get action error: {e}") return safe_json_dumps({"error": str(e)})

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/mlamoure/indigo-mcp-server'

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