Compare commits

..

7 Commits

Author SHA1 Message Date
jango-blockchained
e6c072f6c8 Update README.md to improve Docker setup instructions
- Clarified Docker setup section, indicating that the setup is in progress and directing users to the `docker` branch for the latest changes.
- Added instructions to copy the `.env.example` file to `.env` for environment configuration.
- Enhanced clarity on configuring the environment variables in the `.env` file, emphasizing the need for user-specific values.
2024-12-17 17:01:28 +01:00
jango-blockchained
7fa2fa91ff Update .env.example for development environment configuration
- Changed NODE_ENV from production to development for local testing.
- Updated HASS_HOST and HASS_SOCKET_URL to point to local Home Assistant instance.
- Added PORT and LOG_LEVEL variables for better control over application settings during development.
2024-12-17 16:49:08 +01:00
jango-blockchained
64cdb6e205 Enhance README.md with new features and project updates
- Added sections for Docker containerization, Jest testing setup, TypeScript integration, and Home Assistant API integration.
- Improved organization of the "In Progress" and "Planned" sections, detailing ongoing and future enhancements.
- Expanded on custom prompt testing, macOS integration, type safety improvements, and testing coverage.
- Updated project documentation for better clarity and user guidance.
2024-12-17 16:42:15 +01:00
jango-blockchained
e62bf9d31d Enhance README.md for clarity and organization
- Forked from tevonsb/homeassistant-mcp and added relevant badges for license, Node.js, Docker Compose, and NPM versions.
- Expanded the Table of Contents for easier navigation.
- Clarified prerequisites by specifying required tools and their versions.
- Improved installation instructions with formatted code blocks for better readability.
- Enhanced troubleshooting section with clearer issue descriptions and solutions.
- Updated command examples for consistency and added details for environment variable configuration.
2024-12-17 16:36:04 +01:00
jango-blockchained
153195b51e Add Jest configuration and update TypeScript dependency 2024-12-17 16:21:19 +01:00
jango-blockchained
76a22a6649 Add HassEntity interface and improve type safety in device handling 2024-12-17 16:12:12 +01:00
jango-blockchained
66cdd6c7ce Refactor Claude Desktop setup for macOS integration
- Removed the old `claude_desktop_setup.sh` script and replaced it with a new `claude-desktop-macos-setup.sh` script for improved installation and configuration of MCP integration on macOS.
- Enhanced Node.js installation process using Homebrew, ensuring compatibility with version 20.10.0 or higher.
- Added checks for required tools (npm, jq) and streamlined the configuration process, including optional Brave Search integration.
- Improved configuration file generation with better handling of environment variables and permissions.
- Updated user prompts and installation messages for clarity and guidance.
2024-12-17 16:05:35 +01:00
7 changed files with 297 additions and 168 deletions

View File

@@ -1,3 +1,6 @@
NODE_ENV=production
HASS_HOST=your_home_assistant_url
HASS_TOKEN=your_home_assistant_token
NODE_ENV=development
HASS_HOST=http://homeassistant.local:8123
HASS_TOKEN=your_home_assistant_token
PORT=3000
HASS_SOCKET_URL=ws://homeassistant.local:8123/api/websocket
LOG_LEVEL=debug

109
README.md
View File

@@ -1,7 +1,31 @@
# Model Context Protocol Server for Home Assistant
*Forked from [tevonsb/homeassistant-mcp](https://github.com/tevonsb/homeassistant-mcp)*
A powerful bridge between your Home Assistant instance and Language Learning Models (LLMs), enabling natural language control and monitoring of your smart home devices through the Model Context Protocol (MCP).
![License](https://img.shields.io/badge/license-MIT-blue.svg)
![Node.js](https://img.shields.io/badge/node-%3E%3D20.10.0-green.svg)
![Docker Compose](https://img.shields.io/badge/docker-compose-%3E%3D1.27.0-blue.svg)
![NPM](https://img.shields.io/badge/npm-%3E%3D7.0.0-orange.svg)
## Table of Contents
- [Key Features](#key-features)
- [Prerequisites](#prerequisites)
- [Installation](#installation)
- [Basic Setup](#basic-setup)
- [Docker Setup (Recommended)](#docker-setup-recommended)
- [Configuration](#configuration)
- [Development](#development)
- [Supported Commands](#supported-commands)
- [Natural Language Integration](#natural-language-integration)
- [Troubleshooting](#troubleshooting)
- [Project Status](#project-status)
- [Contributing](#contributing)
- [Resources](#resources)
- [License](#license)
## Key Features
- **Smart Device Control** 🎮
@@ -21,9 +45,10 @@ A powerful bridge between your Home Assistant instance and Language Learning Mod
## Prerequisites
- Node.js 20.10.0 or higher
- NPM package manager
- Running Home Assistant instance
- **Node.js** 20.10.0 or higher
- **NPM** package manager
- **Docker Compose** for containerization
- Running **Home Assistant** instance
- Home Assistant long-lived access token ([How to get token](https://community.home-assistant.io/t/how-to-get-long-lived-access-token/162159))
## Installation
@@ -42,34 +67,42 @@ npm install
npm run build
```
### Docker Setup (Recommended)
### Docker Setup
1. Clone and prepare:
```bash
git clone https://github.com/jango-blockchained/homeassistant-mcp.git
cd homeassistant-mcp
```
> Note: This setup is currently in progress. You can use the `docker` branch to get the latest changes.
2. Configure environment:
```env
NODE_ENV=production
HASS_HOST=your_home_assistant_url
HASS_TOKEN=your_home_assistant_token
```
1. **Clone and prepare:**
```bash
git clone -b docker https://github.com/jango-blockchained/homeassistant-mcp.git
cd homeassistant-mcp
cp .env.example .env
```
3. Launch with Docker Compose:
```bash
docker-compose up -d
```
2. **Configure environment `.env` file:**
```env
...
HASS_TOKEN=your_home_assistant_token
...
```
3. **Launch with Docker Compose:**
```bash
docker-compose up -d
```
## Configuration
Create a `.env` file with:
Copy `.env.example` to `.env`.
```bash
cp .env.example .env
```
Configure environment `.env` file:
```env
...
HASS_TOKEN=your_home_assistant_token
HASS_HOST=your_home_assistant_url # e.g., http://homeassistant.local:8123
PORT=3000 # Optional, defaults to 3000
...
```
## Development
@@ -78,7 +111,7 @@ PORT=3000 # Optional, defaults to 3000
npm run dev # Development mode
npm run build # Build project
npm run start # Production mode
npx jest --config=jest.config.js # Run tests
npx jest --config=jest.config.cjs # Run tests
```
## Supported Commands
@@ -220,14 +253,14 @@ npx jest --config=jest.config.js # Run tests
## Troubleshooting
### Common Issues
1. Node.js Version (`toSorted is not a function`)
- Solution: Update to Node.js 20.10.0+
2. Connection Issues
1. **Node.js Version (`toSorted is not a function`)**
- **Solution:** Update to Node.js 20.10.0+
2. **Connection Issues**
- Verify Home Assistant is running
- Check HASS_HOST accessibility
- Check `HASS_HOST` accessibility
- Validate token permissions
3. Entity Control Issues
- Verify entity_id exists
3. **Entity Control Issues**
- Verify `entity_id` exists
- Check entity domain matches command
- Ensure parameter values are valid
@@ -238,11 +271,27 @@ npx jest --config=jest.config.js # Run tests
- Device control (Lights, Climate, Covers, Switches, Contacts)
- Basic state management
- Error handling and validation
- Docker containerization and configuration
- Jest testing setup and TypeScript integration
- Environment variable management
- Home Assistant API integration
- Project documentation and README organization
🚧 **In Progress**
- Custom prompt testing
- Custom prompt testing and optimization
- Resource context integration
- Tool organization optimization
- Enhanced macOS integration
- Type safety improvements
- Testing coverage expansion
🔜 **Planned**
- Multi-platform desktop integration
- Advanced error recovery mechanisms
- Performance optimization
- WebSocket implementation for real-time updates
- Enhanced security features
- API documentation generation
## Contributing

View File

@@ -0,0 +1,152 @@
#!/bin/bash
# macos-setup.sh
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
BLUE='\033[0;34m'
NC='\033[0m'
echo -e "${BLUE}Setting up MCP Integration for Claude Desktop${NC}"
# Function to compare version numbers
version_greater_equal() {
printf '%s\n' "$2" "$1" | sort -V -C
}
# Check if Homebrew is installed
if ! command -v brew &> /dev/null; then
echo -e "${RED}Homebrew is not installed. Installing Homebrew...${NC}"
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
fi
# Check if Node.js is installed
if ! command -v node &> /dev/null; then
echo -e "${RED}Node.js is not installed. Installing via Homebrew...${NC}"
brew install node@20
brew link node@20
else
NODE_VERSION=$(node -v)
if ! version_greater_equal "${NODE_VERSION//v/}" "20.10.0"; then
echo -e "${RED}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
brew link node@20
fi
fi
# Check if npm is installed
if ! command -v npm &> /dev/null; then
echo -e "${RED}npm is not installed. Please install npm and try again.${NC}"
exit 1
fi
# Check if jq is installed
if ! command -v jq &> /dev/null; then
echo -e "${RED}jq is not installed. Installing via Homebrew...${NC}"
brew install jq
fi
# Create MCP directory if it doesn't exist
MCP_DIR="$HOME/.mcp"
mkdir -p "$MCP_DIR"
# Clone the Home Assistant MCP repository
echo -e "${BLUE}Cloning Home Assistant MCP repository...${NC}"
git clone https://github.com/jango-blockchained/homeassistant-mcp.git "$MCP_DIR/homeassistant-mcp"
cd "$MCP_DIR/homeassistant-mcp"
# Install dependencies and build
echo -e "${BLUE}Installing dependencies and building...${NC}"
npm install
npm run build
# Create Claude Desktop config directory (macOS specific path)
CLAUDE_CONFIG_DIR="$HOME/Library/Application Support/Claude"
mkdir -p "$CLAUDE_CONFIG_DIR"
# Prompt for configurations
echo -e "${BLUE}Please enter your configurations:${NC}"
read -p "Home Assistant URL (e.g., http://homeassistant.local:8123): " HASS_HOST
read -p "Home Assistant Long-lived access token: " HASS_TOKEN
# Create .env file for Home Assistant
cat > "$MCP_DIR/homeassistant-mcp/.env" << EOL
NODE_ENV=production
HASS_HOST=$HASS_HOST
HASS_TOKEN=$HASS_TOKEN
PORT=3000
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'",
"NODE_ENV": "production",
"PORT": "3000"
}
}
}
}'
# Prompt for enabling Brave Search
read -p "Do you want to enable Brave Search integration? (y/n): " ENABLE_BRAVE_SEARCH
if [[ $ENABLE_BRAVE_SEARCH =~ ^[Yy]$ ]]; then
# Install Brave Search MCP globally only if enabled
echo -e "${BLUE}Installing Brave Search MCP...${NC}"
npm install -g @modelcontextprotocol/server-brave-search
read -p "Brave Search API Key: " BRAVE_API_KEY
# Add Brave Search to the configuration
CONFIG_JSON=$(echo $CONFIG_JSON | jq '.mcpServers += {
"brave-search": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-brave-search"],
"env": {
"BRAVE_API_KEY": "'$BRAVE_API_KEY'"
}
}
}')
fi
# Write the final configuration to file
echo $CONFIG_JSON | jq '.' > "$CLAUDE_CONFIG_DIR/claude_desktop_config.json"
# Set proper permissions
chmod 600 "$CLAUDE_CONFIG_DIR/claude_desktop_config.json"
chmod 600 "$MCP_DIR/homeassistant-mcp/.env"
echo -e "${GREEN}Installation complete!${NC}"
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"
if [[ $ENABLE_BRAVE_SEARCH =~ ^[Yy]$ ]]; then
echo "4. Brave Search MCP integration is also available"
fi
echo -e "${RED}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}"
node "$MCP_DIR/homeassistant-mcp/dist/index.js" test
if [[ $ENABLE_BRAVE_SEARCH =~ ^[Yy]$ ]]; then
echo -e "${BLUE}Testing Brave Search MCP...${NC}"
npx @modelcontextprotocol/server-brave-search test
fi
fi

View File

@@ -1,118 +0,0 @@
#!/bin/bash
# mcp-setup.sh
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
BLUE='\033[0;34m'
NC='\033[0m'
echo -e "${BLUE}Setting up MCP Integration for Claude Desktop${NC}"
# Check if Node.js is installed
if ! command -v node &> /dev/null; then
echo -e "${RED}Node.js is not installed. Installing via nvm...${NC}"
# Install nvm
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash
# Load nvm
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
# Install Node.js 20.10.0
nvm install 20.10.0
nvm use 20.10.0
else
NODE_VERSION=$(node -v)
if [[ ${NODE_VERSION//v/} < "20.10.0" ]]; then
echo -e "${RED}Node.js version must be 20.10.0 or higher. Current version: $NODE_VERSION${NC}"
exit 1
fi
fi
# Install Brave Search MCP globally
echo -e "${BLUE}Installing Brave Search MCP...${NC}"
npm install -g @modelcontextprotocol/server-brave-search
# Create MCP directory if it doesn't exist
MCP_DIR="$HOME/.mcp"
mkdir -p "$MCP_DIR"
# Clone the Home Assistant MCP repository
echo -e "${BLUE}Cloning Home Assistant MCP repository...${NC}"
git clone https://github.com/jango-blockchained/homeassistant-mcp.git "$MCP_DIR/homeassistant-mcp"
cd "$MCP_DIR/homeassistant-mcp"
# Install dependencies and build
npm install
npm run build
# Prompt for configurations
echo -e "${BLUE}Please enter your configurations:${NC}"
read -p "Home Assistant URL (e.g., http://homeassistant.local:8123): " HASS_HOST
read -p "Home Assistant Long-lived access token: " HASS_TOKEN
read -p "Brave Search API Key: " BRAVE_API_KEY
# Create .env file for Home Assistant
cat > "$MCP_DIR/homeassistant-mcp/.env" << EOL
NODE_ENV=production
HASS_HOST=$HASS_HOST
HASS_TOKEN=$HASS_TOKEN
EOL
# Create Claude Desktop config directory
CLAUDE_CONFIG_DIR="$HOME/Library/Application Support/Claude"
mkdir -p "$CLAUDE_CONFIG_DIR"
# Create combined configuration file
cat > "$CLAUDE_CONFIG_DIR/claude_desktop_config.json" << EOL
{
"mcpServers": {
"homeassistant": {
"command": "node",
"args": [
"$MCP_DIR/homeassistant-mcp/dist/index.js"
],
"env": {
"HASS_TOKEN": "$HASS_TOKEN",
"HASS_HOST": "$HASS_HOST"
}
},
"brave-search": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-brave-search"
],
"env": {
"BRAVE_API_KEY": "$BRAVE_API_KEY"
}
}
}
}
EOL
# Set proper permissions
chmod 600 "$CLAUDE_CONFIG_DIR/claude_desktop_config.json"
chmod 600 "$MCP_DIR/homeassistant-mcp/.env"
echo -e "${GREEN}Installation complete!${NC}"
echo -e "${BLUE}Configuration file created at:${NC} $CLAUDE_CONFIG_DIR/claude_desktop_config.json"
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. Both Home Assistant and Brave Search MCP integrations should now be available"
echo -e "${RED}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}"
node "$MCP_DIR/homeassistant-mcp/dist/index.js" test
echo -e "${BLUE}Testing Brave Search MCP...${NC}"
npx @modelcontextprotocol/server-brave-search test
fi

43
jest.config.cjs Normal file
View File

@@ -0,0 +1,43 @@
/** @type {import('ts-jest').JestConfigWithTsJest} */
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
moduleNameMapper: {
'^@src/(.*)$': '<rootDir>/src/$1',
'^@tests/(.*)$': '<rootDir>/__tests__/$1',
'^(\\.{1,2}/.*)\\.js$': '$1'
},
roots: [
'<rootDir>/src',
'<rootDir>/__tests__'
],
transform: {
'^.+\\.tsx?$': ['ts-jest', {
useESM: true,
tsconfig: './tsconfig.json'
}]
},
extensionsToTreatAsEsm: ['.ts', '.tsx'],
testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.tsx?$',
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
resolver: '<rootDir>/jest-resolver.cjs',
transformIgnorePatterns: [
'node_modules/(?!(@digital-alchemy|litemcp|semver|zod)/)'
],
modulePathIgnorePatterns: [
'<rootDir>/dist/'
],
testEnvironmentOptions: {
experimentalVmModules: true
},
setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
globals: {
'ts-jest': {
useESM: true,
tsconfig: {
allowJs: true,
esModuleInterop: true
}
}
}
};

View File

@@ -30,7 +30,7 @@
"semver": "^6.3.1",
"ts-jest": "^28.0.8",
"tsx": "^4.19.2",
"typescript": "^4.9.5"
"typescript": "^5.7.2"
},
"author": "Jango Blockchained",
"license": "MIT"

View File

@@ -33,6 +33,19 @@ const commonCommands = ['turn_on', 'turn_off', 'toggle'] as const;
const coverCommands = [...commonCommands, 'open', 'close', 'stop', 'set_position', 'set_tilt_position'] as const;
const climateCommands = [...commonCommands, 'set_temperature', 'set_hvac_mode', 'set_fan_mode', 'set_humidity'] as const;
interface HassEntity {
entity_id: string;
state: string;
attributes: Record<string, any>;
last_changed?: string;
last_updated?: string;
context?: {
id: string;
parent_id?: string;
user_id?: string;
};
}
async function main() {
const hass = await get_hass();
@@ -57,8 +70,8 @@ async function main() {
throw new Error(`Failed to fetch devices: ${response.statusText}`);
}
const states = await response.json();
const devices = states.reduce((acc: any, state: any) => {
const states = await response.json() as HassEntity[];
const devices = states.reduce((acc: Record<string, HassEntity[]>, state: HassEntity) => {
const domain = state.entity_id.split('.')[0];
if (!acc[domain]) {
acc[domain] = [];
@@ -221,20 +234,7 @@ async function main() {
// Start the server
await server.start();
console.log('MCP Server started successfully and is ready to accept commands.');
// Add more detailed logging for server actions
server.on('toolAdded', (tool) => {
console.log(`Tool added: ${tool.name} - ${tool.description}`);
});
server.on('toolExecuted', (tool, result) => {
console.log(`Tool executed: ${tool.name} - Result: ${result.success ? 'Success' : 'Failure'}`);
if (!result.success) {
console.error(`Error: ${result.message}`);
}
});
console.log('Listening for incoming connections...');
console.log('MCP Server started');
}
main().catch(console.error);