Skip to main content
Glama
pedrof
by pedrof
tools.py16.2 kB
""" MCP tool definitions for Philips Hue control. This module defines all the MCP tools that will be exposed by the server. """ from typing import Any, Dict, List from .hue_client import ( HueClient, BRIGHTNESS_MIN_HUE_API, BRIGHTNESS_MAX_HUE_API, COLOR_TEMP_MIN_MIREDS, COLOR_TEMP_MAX_MIREDS, CIE_XY_MIN, CIE_XY_MAX, ) # Validation helper functions def _validate_light_id(light_id: Any) -> None: """ Validate that a light ID is provided. Args: light_id: The light ID to validate Raises: ValueError: If light_id is None or empty """ if not light_id: raise ValueError("light_id is required") def _validate_brightness(brightness: Any) -> None: """ Validate brightness value is within acceptable range. Args: brightness: The brightness value to validate Raises: ValueError: If brightness is not a number or out of range (0-254) """ if not isinstance(brightness, (int, float)): raise ValueError( f"brightness must be a number, got {type(brightness).__name__}" ) if brightness < BRIGHTNESS_MIN_HUE_API or brightness > BRIGHTNESS_MAX_HUE_API: raise ValueError( f"brightness must be between {BRIGHTNESS_MIN_HUE_API} and " f"{BRIGHTNESS_MAX_HUE_API}" ) def _validate_color_temperature(color_temp: Any) -> None: """ Validate color temperature value is within acceptable range. Args: color_temp: The color temperature value to validate Raises: ValueError: If color_temp is not a number or out of range (153-500 mireds) """ if not isinstance(color_temp, (int, float)): raise ValueError( f"color_temp must be a number, got {type(color_temp).__name__}" ) if color_temp < COLOR_TEMP_MIN_MIREDS or color_temp > COLOR_TEMP_MAX_MIREDS: raise ValueError( f"color_temp must be between {COLOR_TEMP_MIN_MIREDS} and " f"{COLOR_TEMP_MAX_MIREDS} mireds" ) def _validate_xy_coordinates(xy: Any) -> None: """ Validate CIE xy color coordinates. Args: xy: The xy coordinates to validate Raises: ValueError: If xy is not a valid list of two floats between 0 and 1 """ if not isinstance(xy, list): raise ValueError(f"xy must be a list, got {type(xy).__name__}") if len(xy) != 2: raise ValueError(f"xy must be a list of two values [x, y], got {len(xy)} values") for i, value in enumerate(xy): coordinate_name = "x" if i == 0 else "y" if not isinstance(value, (int, float)): raise ValueError( f"xy[{i}] ({coordinate_name}) must be a number, got {type(value).__name__}" ) if value < CIE_XY_MIN or value > CIE_XY_MAX: raise ValueError( f"xy[{i}] ({coordinate_name}) must be between {CIE_XY_MIN} and " f"{CIE_XY_MAX}" ) async def list_lights_tool(client: HueClient, arguments: Dict[str, Any]) -> List[Dict[str, Any]]: """ List all lights connected to the Hue Bridge. Returns: List of lights with their current state """ lights = await client.get_lights() return [ { "id": light_id, "name": light_data["name"], "on": light_data["on"], "brightness": light_data["brightness"], "color_temp": light_data["color_temp"], "reachable": light_data["reachable"], } for light_id, light_data in lights.items() ] async def get_light_state_tool(client: HueClient, arguments: Dict[str, Any]) -> Dict[str, Any]: """ Get the current state of a specific light. Args: arguments: Must contain 'light_id' key Returns: Detailed state of the light """ light_id = arguments.get("light_id") _validate_light_id(light_id) light = await client.get_light(light_id) if not light: raise ValueError(f"Light with ID '{light_id}' not found") return light async def turn_light_on_tool(client: HueClient, arguments: Dict[str, Any]) -> Dict[str, Any]: """ Turn a light on, optionally setting brightness. Args: arguments: Must contain 'light_id', optionally 'brightness' (0-254) Returns: Success message """ light_id = arguments.get("light_id") brightness = arguments.get("brightness") _validate_light_id(light_id) if brightness is not None: _validate_brightness(brightness) await client.set_light_state(light_id, on=True, brightness=brightness) brightness_message = f" with brightness {brightness}" if brightness is not None else "" return { "success": True, "message": f"Light {light_id} turned on{brightness_message}", } async def turn_light_off_tool(client: HueClient, arguments: Dict[str, Any]) -> Dict[str, Any]: """ Turn a light off. Args: arguments: Must contain 'light_id' Returns: Success message """ light_id = arguments.get("light_id") _validate_light_id(light_id) await client.set_light_state(light_id, on=False) return {"success": True, "message": f"Light {light_id} turned off"} async def set_brightness_tool(client: HueClient, arguments: Dict[str, Any]) -> Dict[str, Any]: """ Set the brightness of a light. Args: arguments: Must contain 'light_id' and 'brightness' (0-254) Returns: Success message """ light_id = arguments.get("light_id") brightness = arguments.get("brightness") _validate_light_id(light_id) if brightness is None: raise ValueError("brightness is required") _validate_brightness(brightness) await client.set_light_state(light_id, brightness=int(brightness)) return {"success": True, "message": f"Light {light_id} brightness set to {brightness}"} async def set_color_temp_tool(client: HueClient, arguments: Dict[str, Any]) -> Dict[str, Any]: """ Set the color temperature of a light. Args: arguments: Must contain 'light_id' and 'color_temp' (153-500 mireds) Returns: Success message """ light_id = arguments.get("light_id") color_temp = arguments.get("color_temp") _validate_light_id(light_id) if color_temp is None: raise ValueError("color_temp is required") _validate_color_temperature(color_temp) await client.set_light_state(light_id, color_temp=int(color_temp)) return {"success": True, "message": f"Light {light_id} color temperature set to {color_temp} mireds"} async def set_color_tool(client: HueClient, arguments: Dict[str, Any]) -> Dict[str, Any]: """ Set the color of a light using CIE xy coordinates. Args: arguments: Must contain 'light_id' and 'xy' (list of two floats [x, y]) Returns: Success message """ light_id = arguments.get("light_id") xy = arguments.get("xy") _validate_light_id(light_id) if not xy: raise ValueError("xy is required") _validate_xy_coordinates(xy) await client.set_light_state(light_id, xy=xy) return {"success": True, "message": f"Light {light_id} color set to xy={xy}"} async def list_groups_tool(client: HueClient, arguments: Dict[str, Any]) -> List[Dict[str, Any]]: """ List all groups/rooms. Returns: List of groups with their properties """ groups = await client.get_groups() return [ { "id": group_id, "name": group_data["name"], "type": group_data["type"], "lights": group_data["lights"], } for group_id, group_data in groups.items() ] async def control_group_tool(client: HueClient, arguments: Dict[str, Any]) -> Dict[str, Any]: """ Control all lights in a group. Args: arguments: Must contain 'group_id', optionally 'on', 'brightness', 'color_temp', 'xy' Returns: Success message """ group_id = arguments.get("group_id") on = arguments.get("on") brightness = arguments.get("brightness") color_temp = arguments.get("color_temp") xy = arguments.get("xy") if not group_id: raise ValueError("group_id is required") # Validate optional parameters if provided if brightness is not None: _validate_brightness(brightness) if color_temp is not None: _validate_color_temperature(color_temp) if xy is not None: _validate_xy_coordinates(xy) await client.set_group_state( group_id=group_id, on=on, brightness=int(brightness) if brightness is not None else None, color_temp=int(color_temp) if color_temp is not None else None, xy=xy, ) return {"success": True, "message": f"Group {group_id} state updated"} async def list_scenes_tool(client: HueClient, arguments: Dict[str, Any]) -> List[Dict[str, Any]]: """ List all available scenes. Returns: List of scenes with their properties """ scenes = await client.get_scenes() return [ { "id": scene_id, "name": scene_data["name"], "group": scene_data["group"], } for scene_id, scene_data in scenes.items() ] async def activate_scene_tool(client: HueClient, arguments: Dict[str, Any]) -> Dict[str, Any]: """ Activate a scene. Args: arguments: Must contain 'scene_id' Returns: Success message """ scene_id = arguments.get("scene_id") if not scene_id: raise ValueError("scene_id is required") await client.activate_scene(scene_id) return {"success": True, "message": f"Scene {scene_id} activated"} TOOLS = { "list_lights": { "function": list_lights_tool, "description": "List all lights connected to the Hue Bridge with their current state", "parameters": { "type": "object", "properties": {}, "required": [], }, }, "get_light_state": { "function": get_light_state_tool, "description": "Get detailed state information for a specific light", "parameters": { "type": "object", "properties": { "light_id": { "type": "string", "description": "The unique identifier of the light", } }, "required": ["light_id"], }, }, "turn_light_on": { "function": turn_light_on_tool, "description": "Turn a light on, optionally setting brightness", "parameters": { "type": "object", "properties": { "light_id": { "type": "string", "description": "The unique identifier of the light", }, "brightness": { "type": "number", "description": "Optional brightness level (0-254)", "minimum": 0, "maximum": 254, }, }, "required": ["light_id"], }, }, "turn_light_off": { "function": turn_light_off_tool, "description": "Turn a light off", "parameters": { "type": "object", "properties": { "light_id": { "type": "string", "description": "The unique identifier of the light", } }, "required": ["light_id"], }, }, "set_brightness": { "function": set_brightness_tool, "description": "Set the brightness level of a light", "parameters": { "type": "object", "properties": { "light_id": { "type": "string", "description": "The unique identifier of the light", }, "brightness": { "type": "number", "description": "Brightness level (0-254, where 0 is minimum and 254 is maximum)", "minimum": 0, "maximum": 254, }, }, "required": ["light_id", "brightness"], }, }, "set_color_temp": { "function": set_color_temp_tool, "description": "Set the color temperature of a light in mireds (153=cold, 500=warm)", "parameters": { "type": "object", "properties": { "light_id": { "type": "string", "description": "The unique identifier of the light", }, "color_temp": { "type": "number", "description": "Color temperature in mireds (153-500, where 153 is coldest and 500 is warmest)", "minimum": 153, "maximum": 500, }, }, "required": ["light_id", "color_temp"], }, }, "set_color": { "function": set_color_tool, "description": "Set the color of a light using CIE xy color space coordinates", "parameters": { "type": "object", "properties": { "light_id": { "type": "string", "description": "The unique identifier of the light", }, "xy": { "type": "array", "description": "CIE xy color coordinates as [x, y], each value between 0 and 1", "items": {"type": "number", "minimum": 0, "maximum": 1}, "minItems": 2, "maxItems": 2, }, }, "required": ["light_id", "xy"], }, }, "list_groups": { "function": list_groups_tool, "description": "List all groups/rooms with their properties", "parameters": { "type": "object", "properties": {}, "required": [], }, }, "control_group": { "function": control_group_tool, "description": "Control all lights in a group/room at once", "parameters": { "type": "object", "properties": { "group_id": { "type": "string", "description": "The unique identifier of the group", }, "on": { "type": "boolean", "description": "Turn lights on (true) or off (false)", }, "brightness": { "type": "number", "description": "Optional brightness level (0-254)", "minimum": 0, "maximum": 254, }, "color_temp": { "type": "number", "description": "Optional color temperature in mireds (153-500)", "minimum": 153, "maximum": 500, }, "xy": { "type": "array", "description": "Optional CIE xy color coordinates as [x, y]", "items": {"type": "number", "minimum": 0, "maximum": 1}, "minItems": 2, "maxItems": 2, }, }, "required": ["group_id"], }, }, "list_scenes": { "function": list_scenes_tool, "description": "List all available scenes", "parameters": { "type": "object", "properties": {}, "required": [], }, }, "activate_scene": { "function": activate_scene_tool, "description": "Activate a predefined scene", "parameters": { "type": "object", "properties": { "scene_id": { "type": "string", "description": "The unique identifier of the scene", } }, "required": ["scene_id"], }, }, }

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/pedrof/hue-mcp-server'

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