| name | homeassistant-mcp-server |
| description | Control Home Assistant smart home devices through AI assistants using the Model Context Protocol |
| triggers | ["control my home assistant devices","set up home assistant mcp server","automate my smart home with ai","connect claude to home assistant","use mcp with home assistant","control lights and climate through ai","query home assistant device states","create home assistant automations with ai"] |
Home Assistant MCP Server
Skill by ara.so — MCP Skills collection
Overview
The Home Assistant MCP Server enables AI assistants (Claude, GPT-4, Cursor, etc.) to control smart home devices through the Model Context Protocol. It provides 40 tools covering lights, climate, covers, locks, media players, vacuums, and more, with BM25 tool search to keep context lean and MCP resources for read-only data access.
Built with Python and FastMCP, it uses async operations with TTL-based caching to minimize API load.
Installation
uvx (Recommended - No Installation Required)
Configure your MCP client with:
{
"mcpServers": {
"homeassistant": {
"command": "uvx",
"args": ["homeassistant-mcp"],
"env": {
"HASS_HOST": "http://homeassistant.local:8123",
"HASS_TOKEN": "your_long_lived_access_token_here"
}
}
}
}
pip Installation
pip install homeassistant-mcp
Run with:
export HASS_HOST="http://homeassistant.local:8123"
export HASS_TOKEN="your_token_here"
homeassistant-mcp
From Source
git clone https://github.com/robbrad/homeassistant-mcp.git
cd homeassistant-mcp
python -m venv venv
source venv/bin/activate
pip install -e ".[dev]"
homeassistant-mcp
Configuration
Required Environment Variables
| Variable | Description | Example |
|---|
HASS_HOST | Home Assistant URL | http://homeassistant.local:8123 |
HASS_TOKEN | Long-lived access token | Generate from HA profile page |
Optional Environment Variables
| Variable | Default | Description |
|---|
CACHE_TTL_STATES | 30 | Cache TTL for bulk state queries (seconds) |
CACHE_TTL_ENTITY | 10 | Cache TTL for individual entity queries (seconds) |
LOG_LEVEL | INFO | Logging level (DEBUG, INFO, WARNING, ERROR) |
Claude Desktop Configuration
macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
Windows: %APPDATA%\Claude\claude_desktop_config.json
{
"mcpServers": {
"homeassistant": {
"command": "uvx",
"args": ["homeassistant-mcp"],
"env": {
"HASS_HOST": "http://homeassistant.local:8123",
"HASS_TOKEN": "eyJ0eXAiOiJKV1QiLCJhbGc..."
}
}
}
}
Cursor Configuration
.cursor/mcp.json:
{
"mcpServers": {
"homeassistant": {
"command": "uvx",
"args": ["homeassistant-mcp"],
"env": {
"HASS_HOST": "${HASS_HOST}",
"HASS_TOKEN": "${HASS_TOKEN}"
}
}
}
}
Tool Discovery System
The server exposes 8 core tools for tool discovery instead of all 40 upfront:
Core Discovery Tools
discover_tools()
search_tools(query="lights brightness")
call_tool(tool_name="lights_control", arguments={...})
Example Tool Discovery Flow
tools = discover_tools()
results = search_tools(query="control lights and brightness")
lights_control(
action="turn_on",
entity_id="light.living_room",
brightness=80,
color_temp=370
)
Key Tools by Category
Device Control Tools (16 total)
Light Control
lights_control(
action="turn_on",
entity_id="light.bedroom",
brightness=100,
rgb_color=[255, 0, 0]
)
lights_control(
action="turn_on",
entity_id="light.kitchen",
color_temp=370
)
lights_control(action="turn_off", entity_id="light.bedroom")
lights_control(
action="turn_on",
entity_id="light.all_lights",
brightness=50
)
Climate Control
climate_control(
action="set_temperature",
entity_id="climate.living_room",
temperature=22,
hvac_mode="heat"
)
climate_control(
action="set_fan_mode",
entity_id="climate.bedroom",
fan_mode="auto"
)
climate_control(action="get", entity_id="climate.living_room")
Cover Control
cover_control(
action="set_position",
entity_id="cover.garage_door",
position=50
)
cover_control(action="open", entity_id="cover.living_room_blinds")
cover_control(action="close", entity_id="cover.bedroom_blinds")
cover_control(
action="set_tilt",
entity_id="cover.venetian_blinds",
tilt=45
)
Lock Control
lock_control(action="lock", entity_id="lock.front_door")
lock_control(action="unlock", entity_id="lock.back_door", code="1234")
lock_control(action="get", entity_id="lock.front_door")
Media Player Control
media_player_control(action="play", entity_id="media_player.living_room_tv")
media_player_control(action="pause", entity_id="media_player.spotify")
media_player_control(
action="volume_set",
entity_id="media_player.speakers",
volume=0.5
)
media_player_control(
action="select_source",
entity_id="media_player.receiver",
source="HDMI 1"
)
Vacuum Control
vacuum_control(action="start", entity_id="vacuum.roborock")
vacuum_control(action="dock", entity_id="vacuum.roborock")
vacuum_control(
action="set_fan_speed",
entity_id="vacuum.roborock",
fan_speed="turbo"
)
State Management
states_control(action="list")
states_control(action="list", domain="light")
states_control(action="get", entity_id="sensor.temperature")
states_control(
action="set",
entity_id="sensor.custom_sensor",
state="active",
attributes={"battery": 95}
)
states_control(action="delete", entity_id="sensor.old_sensor")
Device Discovery
list_devices()
list_devices(domain="light")
list_devices(area="living_room")
list_devices(floor="ground_floor")
list_devices(domain="switch", area="bedroom")
Service Calls
call_service(
domain="light",
service="turn_on",
service_data={
"entity_id": "light.bedroom",
"brightness": 80,
"rgb_color": [255, 120, 0]
}
)
call_service(
domain="notify",
service="mobile_app_phone",
service_data={
"message": "Motion detected in garage",
"title": "Security Alert"
}
)
call_service(
domain="tts",
service="google_translate_say",
service_data={
"entity_id": "media_player.kitchen_speaker",
"message": "Dinner is ready"
}
)
Automation Control
automation_control(action="list")
automation_control(action="trigger", automation_id="automation.morning_routine")
automation_control(action="enable", automation_id="automation.night_mode")
automation_control(action="disable", automation_id="automation.away_mode")
automation_control(action="reload")
Scene Control
scene_control(action="list")
scene_control(action="activate", scene_id="scene.movie_time")
Script Control
script_control(action="list")
script_control(
action="execute",
script_id="script.notification_routine",
variables={"message": "Door left open", "priority": "high"}
)
script_control(action="reload")
Template Rendering
template_render(
template="{{ states('sensor.temperature') | float + 5 }}"
)
template_render(
template="""
{% set lights = states.light | selectattr('state', 'eq', 'on') | list %}
{{ lights | length }} lights are currently on
"""
)
History & Logbook
history_query(
entity_id="sensor.temperature",
hours=24
)
logbook_query(
entity_id="binary_sensor.front_door",
hours=12
)
error_log_get()
Calendar Access
calendar_access(action="list")
calendar_access(
action="get_events",
calendar_id="calendar.family",
start_date="2024-05-01",
end_date="2024-05-31"
)
Camera Operations
camera_proxy_get(
entity_id="camera.front_door",
width=640,
height=480
)
camera_control(
action="enable_motion_detection",
entity_id="camera.garage"
)
camera_control(
action="snapshot",
entity_id="camera.backyard",
filename="/config/www/snapshots/backyard.jpg"
)
MCP Resources
Resources provide read-only access to Home Assistant data via URIs.
Entity Resource
hass://entity/light.living_room
Area Resource
hass://area/living_room
Device Resource
hass://device/abc123def456
Services Resource
hass://services
Entity History Resource
hass://entity/sensor.temperature/history?hours=24
Common Patterns
Morning Routine Automation
tools = discover_tools()
scene_control(action="activate", scene_id="scene.morning")
climate_control(
action="set_temperature",
entity_id="climate.bedroom",
temperature=21,
hvac_mode="heat"
)
cover_control(action="open", entity_id="cover.bedroom_blinds")
lights_control(
action="turn_on",
entity_id="light.bedroom",
brightness=30,
transition=60
)
Security Check Routine
locks = list_devices(domain="lock")
for lock in locks:
state = lock_control(action="get", entity_id=lock["entity_id"])
if state["state"] == "unlocked":
send_notification(
message=f"{lock['name']} is unlocked!",
title="Security Alert"
)
doors = list_devices(domain="binary_sensor", area="entry")
for door in doors:
if "door" in door["entity_id"]:
state = states_control(action="get", entity_id=door["entity_id"])
Energy Optimization
lights = states_control(action="list", domain="light")
for light in lights:
if light["state"] == "on":
presence = states_control(action="get", entity_id="binary_sensor.occupancy")
if presence["state"] == "off":
lights_control(action="turn_off", entity_id=light["entity_id"])
climate_control(
action="set_temperature",
entity_id="climate.all",
temperature=19,
hvac_mode="heat"
)
Media Room Scene
lights_control(
action="turn_on",
entity_id="light.media_room",
brightness=10,
rgb_color=[255, 100, 0]
)
cover_control(action="close", entity_id="cover.media_room_blinds")
media_player_control(
action="select_source",
entity_id="media_player.tv",
source="Netflix"
)
media_player_control(action="play", entity_id="media_player.tv")
Conditional Device Control
temp_state = states_control(action="get", entity_id="sensor.temperature")
current_temp = float(temp_state["state"])
if current_temp < 18:
climate_control(
action="set_temperature",
entity_id="climate.living_room",
temperature=22,
hvac_mode="heat"
)
lux_state = states_control(action="get", entity_id="sensor.illuminance")
if float(lux_state["state"]) < 50:
lights_control(action="turn_on", entity_id="light.all_lights")
Development
Running Tests
pip install -e ".[dev]"
pytest
pytest --cov=src --cov-report=html
Code Quality
black src/ tests/
ruff check src/ tests/
mypy src/
Local Development
git clone https://github.com/robbrad/homeassistant-mcp.git
cd homeassistant-mcp
python -m venv venv
source venv/bin/activate
pip install -e ".[dev]"
cat > .env << EOF
HASS_HOST=http://homeassistant.local:8123
HASS_TOKEN=your_token_here
LOG_LEVEL=DEBUG
EOF
homeassistant-mcp
Adding New Tools
from fastmcp import FastMCP
mcp = FastMCP("homeassistant")
@mcp.tool(
description="Control custom device",
tags=["device", "custom"],
timeout=30.0
)
async def custom_device_control(
action: str,
entity_id: str,
value: int = 0
) -> dict:
"""
Control custom device
Args:
action: Action to perform (on, off, set)
entity_id: Entity ID
value: Optional value parameter
"""
client = get_hass_client()
return await client.call_service(
domain="custom_domain",
service=action,
service_data={"entity_id": entity_id, "value": value}
)
Troubleshooting
Connection Issues
api_info()
Entity Not Found
all_entities = states_control(action="list")
lights = states_control(action="list", domain="light")
devices = list_devices(area="bedroom")
Service Call Failures
services = services_control(action="list", domain="light")
schema = services_control(action="get", domain="light", service="turn_on")
config_check()
Tool Not Available
all_tools = discover_tools()
matching_tools = search_tools(query="your search query")
call_tool(tool_name="tool_name", arguments={...})
Cache Issues
export CACHE_TTL_STATES=5
export CACHE_TTL_ENTITY=2
export LOG_LEVEL=DEBUG
Error Log Analysis
errors = error_log_get()
Authentication Errors
- Verify
HASS_TOKEN is a long-lived access token (not temporary)
- Create new token: HA Profile → Long-Lived Access Tokens → Create Token
- Check token has required permissions for device domains
Rate Limiting
The server uses TTL-based caching to reduce API load:
- Bulk state queries cached for 30s (configurable via
CACHE_TTL_STATES)
- Individual entity queries cached for 10s (configurable via
CACHE_TTL_ENTITY)
- Adjust cache TTLs based on your update frequency needs