Skip to main content
Glama
pedrof
by pedrof
server.py6.12 kB
""" Main MCP server for Philips Hue control. This module implements the MCP server that exposes Hue control tools. """ import asyncio import json import logging import os import sys from typing import Any from dotenv import load_dotenv from mcp.server import Server from mcp.server.stdio import stdio_server from mcp.types import Tool, TextContent from .hue_client import HueClient from .tools import TOOLS logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) # Environment variable names ENV_VAR_BRIDGE_IP = "HUE_BRIDGE_IP" ENV_VAR_API_KEY = "HUE_API_KEY" # Server name SERVER_NAME = "hue-mcp-server" class HueMCPServer: """MCP Server for Philips Hue control.""" def __init__(self, bridge_ip: str, api_key: str): """ Initialize the Hue MCP server. Args: bridge_ip: IP address of the Hue Bridge api_key: API key for authentication """ self.server = Server(SERVER_NAME) self.client = HueClient(bridge_ip, api_key) self._setup_handlers() def _ensure_client_connected(self) -> bool: """ Check if client is connected to the bridge. Returns: True if connected, False otherwise """ return self.client.is_connected() async def _connect_if_needed(self) -> None: """ Ensure the client is connected to the bridge. Connects if not already connected. """ if not self._ensure_client_connected(): await self.client.connect() def _setup_handlers(self): """Set up MCP server handlers.""" @self.server.list_tools() async def list_tools() -> list[Tool]: """List available tools.""" return [ Tool( name=name, description=tool_def["description"], inputSchema=tool_def["parameters"], ) for name, tool_def in TOOLS.items() ] @self.server.call_tool() async def call_tool(name: str, arguments: Any) -> list[TextContent]: """ Handle tool calls from the MCP client. Args: name: The name of the tool to execute arguments: The arguments to pass to the tool Returns: List of TextContent containing the result or error message """ if name not in TOOLS: error_message = f"Unknown tool: '{name}'. Available tools: {', '.join(TOOLS.keys())}" logger.error(error_message) return [TextContent(type="text", text=error_message)] tool_def = TOOLS[name] tool_function = tool_def["function"] try: # Ensure we're connected to the bridge await self._connect_if_needed() # Call the tool function result = await tool_function(self.client, arguments or {}) # Format the result as JSON for readability result_text = json.dumps(result, indent=2) return [TextContent(type="text", text=result_text)] except ValueError as e: # Validation errors from tool functions error_message = f"Validation error in '{name}': {str(e)}" logger.error(error_message) return [TextContent(type="text", text=error_message)] except RuntimeError as e: # Runtime errors (e.g., connection issues) error_message = f"Runtime error in '{name}': {str(e)}" logger.error(error_message, exc_info=True) return [TextContent(type="text", text=error_message)] except Exception as e: # Catch-all for unexpected errors error_message = f"Unexpected error executing '{name}': {str(e)}" logger.error(error_message, exc_info=True) return [TextContent(type="text", text=error_message)] async def run(self): """Run the MCP server.""" try: # Connect to the Hue Bridge await self.client.connect() logger.info("Hue MCP Server started successfully") # Run the server async with stdio_server() as (read_stream, write_stream): await self.server.run( read_stream, write_stream, self.server.create_initialization_options(), ) except KeyboardInterrupt: logger.info("Server interrupted by user") except Exception as e: logger.error(f"Server error: {e}", exc_info=True) raise finally: await self.client.disconnect() logger.info("Hue MCP Server stopped") def _load_configuration() -> tuple[str, str]: """ Load and validate configuration from environment variables. Returns: Tuple of (bridge_ip, api_key) Raises: SystemExit: If required environment variables are missing """ load_dotenv() bridge_ip = os.getenv(ENV_VAR_BRIDGE_IP) api_key = os.getenv(ENV_VAR_API_KEY) if not bridge_ip: logger.error( f"Missing required environment variable: {ENV_VAR_BRIDGE_IP}. " f"Please set it in your .env file or environment." ) sys.exit(1) if not api_key: logger.error( f"Missing required environment variable: {ENV_VAR_API_KEY}. " f"Please set it in your .env file or environment." ) sys.exit(1) logger.info(f"Configuration loaded: Bridge IP = {bridge_ip}") return bridge_ip, api_key def main(): """ Main entry point for the Hue MCP server. Loads configuration from environment variables and starts the server. """ bridge_ip, api_key = _load_configuration() # Create and run the server server = HueMCPServer(bridge_ip, api_key) asyncio.run(server.run()) if __name__ == "__main__": main()

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