HomeAssistant MCP

#!/bin/bash # macos-setup.sh # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' BLUE='\033[0;34m' YELLOW='\033[0;33m' NC='\033[0m' echo -e "${BLUE}Setting up MCP Integration for Claude Desktop${NC}" # Error handling function handle_error() { echo -e "${RED}Error: $1${NC}" exit 1 } # Function to compare version numbers version_greater_equal() { printf '%s\n' "$2" "$1" | sort -V -C } # Function to backup existing configuration backup_config() { local backup_dir="$MCP_DIR/backups/$(date +%Y%m%d_%H%M%S)" echo -e "${BLUE}Creating backup at: $backup_dir${NC}" mkdir -p "$backup_dir" || handle_error "Failed to create backup directory" # Backup existing configurations if they exist if [ -f "$CLAUDE_CONFIG_DIR/claude_desktop_config.json" ]; then cp "$CLAUDE_CONFIG_DIR/claude_desktop_config.json" "$backup_dir/" || handle_error "Failed to backup Claude config" fi if [ -f "$MCP_DIR/homeassistant-mcp/.env" ]; then cp "$MCP_DIR/homeassistant-mcp/.env" "$backup_dir/" || handle_error "Failed to backup .env file" fi echo -e "${GREEN}Backup created successfully${NC}" return 0 } # Function to check for existing installation check_existing_installation() { local has_existing=false local config_exists=false local repo_exists=false if [ -d "$MCP_DIR/homeassistant-mcp" ]; then repo_exists=true fi if [ -f "$CLAUDE_CONFIG_DIR/claude_desktop_config.json" ] || [ -f "$MCP_DIR/homeassistant-mcp/.env" ]; then config_exists=true fi if $repo_exists || $config_exists; then echo -e "${YELLOW}Existing MCP installation detected${NC}" echo -e "Found:" $repo_exists && echo " - MCP repository at $MCP_DIR/homeassistant-mcp" [ -f "$CLAUDE_CONFIG_DIR/claude_desktop_config.json" ] && echo " - Claude Desktop configuration" [ -f "$MCP_DIR/homeassistant-mcp/.env" ] && echo " - Environment configuration" while true; do echo -e "\nPlease choose an option:" echo "1) Upgrade existing installation (preserves configuration)" echo "2) Clean reinstall (backs up and replaces configuration)" echo "3) Exit" read -p "Enter your choice (1-3): " choice case $choice in 1) echo -e "${BLUE}Upgrading existing installation...${NC}" backup_config UPGRADE_MODE=true break ;; 2) echo -e "${BLUE}Performing clean reinstall...${NC}" backup_config # Remove existing installation if [ -d "$MCP_DIR/homeassistant-mcp" ]; then rm -rf "$MCP_DIR/homeassistant-mcp" || handle_error "Failed to remove existing repository" fi if [ -f "$CLAUDE_CONFIG_DIR/claude_desktop_config.json" ]; then rm "$CLAUDE_CONFIG_DIR/claude_desktop_config.json" || handle_error "Failed to remove existing Claude config" fi CLEAN_INSTALL=true break ;; 3) echo -e "${BLUE}Installation cancelled${NC}" exit 0 ;; *) echo -e "${RED}Invalid choice. Please enter 1, 2, or 3${NC}" ;; esac done fi } # Create MCP directory if it doesn't exist MCP_DIR="$HOME/.mcp" mkdir -p "$MCP_DIR" || handle_error "Failed to create MCP directory" # Create Claude Desktop config directory (macOS specific path) CLAUDE_CONFIG_DIR="$HOME/Library/Application Support/Claude" mkdir -p "$CLAUDE_CONFIG_DIR" || handle_error "Failed to create Claude config directory" # Initialize installation mode flags UPGRADE_MODE=false CLEAN_INSTALL=false # Check for existing installation before proceeding check_existing_installation # Check system requirements echo -e "${BLUE}Checking system requirements...${NC}" # Check if Homebrew is installed if ! command -v brew &> /dev/null; then echo -e "${YELLOW}Homebrew is not installed. Installing Homebrew...${NC}" /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" || handle_error "Failed to install Homebrew" fi # Check if Node.js is installed if ! command -v node &> /dev/null; then echo -e "${YELLOW}Node.js is not installed. Installing via Homebrew...${NC}" brew install node@20 || handle_error "Failed to install Node.js" brew link node@20 || handle_error "Failed to link Node.js" else NODE_VERSION=$(node -v) if ! version_greater_equal "${NODE_VERSION//v/}" "20.10.0"; then echo -e "${YELLOW}Node.js version must be 20.10.0 or higher. Current version: $NODE_VERSION${NC}" echo -e "${BLUE}Installing Node.js 20 via Homebrew...${NC}" brew install node@20 || handle_error "Failed to install Node.js" brew link node@20 || handle_error "Failed to link Node.js" fi fi # Check if npm is installed if ! command -v npm &> /dev/null; then handle_error "npm is not installed. Please install npm and try again." fi # Check if jq is installed if ! command -v jq &> /dev/null; then echo -e "${YELLOW}jq is not installed. Installing via Homebrew...${NC}" brew install jq || handle_error "Failed to install jq" fi # Check for required development tools echo -e "${BLUE}Checking development dependencies...${NC}" REQUIRED_TOOLS=("git" "curl" "typescript") for tool in "${REQUIRED_TOOLS[@]}"; do if ! command -v "$tool" &> /dev/null; then case $tool in "typescript") echo -e "${YELLOW}TypeScript is not installed. Installing globally...${NC}" npm install -g typescript || handle_error "Failed to install TypeScript" ;; *) echo -e "${YELLOW}$tool is not installed. Installing via Homebrew...${NC}" brew install "$tool" || handle_error "Failed to install $tool" ;; esac fi done # Clone the Home Assistant MCP repository echo -e "${BLUE}Cloning Home Assistant MCP repository...${NC}" if [ -d "$MCP_DIR/homeassistant-mcp" ] && [ "$CLEAN_INSTALL" = false ]; then echo -e "${YELLOW}Repository exists. Updating...${NC}" cd "$MCP_DIR/homeassistant-mcp" || handle_error "Failed to change directory" git fetch origin || handle_error "Failed to fetch updates" git reset --hard origin/main || handle_error "Failed to reset to main branch" git clean -fd || handle_error "Failed to clean repository" else if [ -d "$MCP_DIR/homeassistant-mcp" ]; then rm -rf "$MCP_DIR/homeassistant-mcp" || handle_error "Failed to remove existing repository" fi git clone https://github.com/jango-blockchained/homeassistant-mcp.git "$MCP_DIR/homeassistant-mcp" || handle_error "Failed to clone repository" cd "$MCP_DIR/homeassistant-mcp" || handle_error "Failed to change directory" fi # Install dependencies and build echo -e "${BLUE}Installing dependencies and building...${NC}" npm ci || handle_error "Failed to install npm dependencies" npm run build || handle_error "Failed to build project" # If upgrading, try to reuse existing configuration if [ "$UPGRADE_MODE" = true ] && [ -f "$CLAUDE_CONFIG_DIR/claude_desktop_config.json" ]; then echo -e "${BLUE}Reusing existing configuration...${NC}" # Extract existing values if [ -f "$MCP_DIR/homeassistant-mcp/.env" ]; then source "$MCP_DIR/homeassistant-mcp/.env" HASS_HOST=${HASS_HOST:-""} HASS_TOKEN=${HASS_TOKEN:-""} HASS_SOCKET_URL=${HASS_SOCKET_URL:-""} fi # If values are missing, prompt for them if [ -z "$HASS_HOST" ] || [ -z "$HASS_TOKEN" ]; then echo -e "${YELLOW}Some configuration values are missing. Please provide them:${NC}" # Get and validate Home Assistant URL while true; do read -p "Home Assistant URL (e.g., http://homeassistant.local:8123): " HASS_HOST if validate_url "$HASS_HOST"; then break fi done # Get and validate Home Assistant token while true; do read -p "Home Assistant Long-lived access token: " HASS_TOKEN if validate_token "$HASS_TOKEN"; then break fi done # Create WebSocket URL from HASS_HOST HASS_SOCKET_URL="${HASS_HOST/http/ws}/api/websocket" fi else # Prompt for configurations with validation echo -e "${BLUE}Please enter your configurations:${NC}" # Function to validate URL validate_url() { if [[ ! "$1" =~ ^https?:// ]]; then handle_error "Invalid URL format. URL must start with http:// or https://" fi } # Function to validate token validate_token() { if [[ -z "$1" ]]; then handle_error "Token cannot be empty" fi } # Get and validate Home Assistant URL while true; do read -p "Home Assistant URL (e.g., http://homeassistant.local:8123): " HASS_HOST if validate_url "$HASS_HOST"; then break fi done # Get and validate Home Assistant token while true; do read -p "Home Assistant Long-lived access token: " HASS_TOKEN if validate_token "$HASS_TOKEN"; then break fi done # Create WebSocket URL from HASS_HOST HASS_SOCKET_URL="${HASS_HOST/http/ws}/api/websocket" # Create .env file for Home Assistant cat > "$MCP_DIR/homeassistant-mcp/.env" << EOL NODE_ENV=production HASS_HOST=$HASS_HOST HASS_TOKEN=$HASS_TOKEN HASS_SOCKET_URL=$HASS_SOCKET_URL PORT=3000 DEBUG=false EOL # Create base configuration for Home Assistant CONFIG_JSON='{ "mcpServers": { "homeassistant": { "command": "node", "args": [ "'$MCP_DIR'/homeassistant-mcp/dist/index.js" ], "env": { "HASS_TOKEN": "'$HASS_TOKEN'", "HASS_HOST": "'$HASS_HOST'", "HASS_SOCKET_URL": "'$HASS_SOCKET_URL'", "NODE_ENV": "production", "PORT": "3000", "DEBUG": "false" } } } }' # Write the final configuration to file echo $CONFIG_JSON | jq '.' > "$CLAUDE_CONFIG_DIR/claude_desktop_config.json" || handle_error "Failed to write configuration file" # Set proper permissions chmod 600 "$CLAUDE_CONFIG_DIR/claude_desktop_config.json" || handle_error "Failed to set permissions on config file" chmod 600 "$MCP_DIR/homeassistant-mcp/.env" || handle_error "Failed to set permissions on .env file" fi echo -e "${GREEN}Installation complete!${NC}" if [ "$UPGRADE_MODE" = true ]; then echo -e "${BLUE}Upgraded existing installation${NC}" elif [ "$CLEAN_INSTALL" = true ]; then echo -e "${BLUE}Performed clean installation${NC}" echo -e "${YELLOW}Your previous configuration has been backed up to: $MCP_DIR/backups/$(ls -t "$MCP_DIR/backups" | head -n1)${NC}" fi echo -e "${BLUE}Configuration files created at:${NC}" echo " - $CLAUDE_CONFIG_DIR/claude_desktop_config.json" echo " - $MCP_DIR/homeassistant-mcp/.env" echo -e "${BLUE}To use the integration:${NC}" echo "1. Make sure Claude Desktop is installed from https://claude.ai/download" echo "2. Restart Claude Desktop" echo "3. Home Assistant MCP integration is now available" echo -e "${YELLOW}Note: Keep your access tokens and API keys secure and never share them with others${NC}" # Optional: Test the installations read -p "Would you like to test the installations? (y/n) " -n 1 -r echo if [[ $REPLY =~ ^[Yy]$ ]]; then echo -e "${BLUE}Testing Home Assistant MCP connection...${NC}" if ! node "$MCP_DIR/homeassistant-mcp/dist/index.js" test; then echo -e "${RED}Home Assistant MCP test failed. Please check your configuration and try again.${NC}" else echo -e "${GREEN}Home Assistant MCP test successful!${NC}" fi fi # macOS environment configuration HASS_SOCKET_URL="${HASS_HOST/http/ws}/api/websocket" # WebSocket URL conversion chmod 600 "$CLAUDE_CONFIG_DIR/claude_desktop_config.json" # Security hardening