hass-mcp-extensions
Provides tools for interacting with a Home Assistant instance, including managing entities, automations, scenes, scripts, Lovelace dashboards, logs, device info, and SSH file access, as well as querying the recorder database for logbook and Zigbee events.
Allows read-only SQL queries against the Home Assistant recorder database, and retrieval of logbook entries and Zigbee events stored in MariaDB.
Supports read-only SQL queries against the Home Assistant recorder database when MySQL is used as the backend, enabling logbook and Zigbee event retrieval.
Enables fetching recent Zigbee device events from the Home Assistant recorder database via the get_zigbee_events tool.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@hass-mcp-extensions fetch my default Lovelace dashboard"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
hass-mcp-extensions
An MCP server that layers additional Home Assistant tools on top of
voska/hass-mcp. The base hass-mcp server
(MIT, © Matt Voska) is vendored under mcp_server/vendor/ so the repo is
self-contained, and extra tools are registered via mcp_server/extensions.py.
See mcp_server/vendor/NOTICE.md for provenance and license.
What this adds
Tool | Description |
| Fetch a full Lovelace dashboard config via WebSocket |
| Fetch a single view from a dashboard by title or path |
| Overwrite an entire Lovelace dashboard config |
| Get a stored automation, scene, or script by numeric ID |
| Fetch Supervisor add-on logs as plain text |
| Fetch the HA Core log (replaces the removed |
| Grep a Supervisor add-on's logs for a pattern |
| Read a file from the HA host over SSH |
| List a directory on the HA host over SSH |
| Create or overwrite a stored automation via the REST API |
| Update a stored scene's entity states |
| Resolve a device's entities, area, and metadata by entity ID or name |
| Run a read-only SQL query against the recorder (MariaDB) database |
| Fetch logbook entries from the recorder database |
| Fetch recent Zigbee device events from the recorder database |
All base tools from the vendored hass-mcp are also available (get_entity,
list_entities, entity_action, call_service_tool, get_history,
get_entities_by_area, restart_ha, etc.).
Related MCP server: hass-mcp-plus
Requirements
Python 3.13+
A running Home Assistant instance with a long-lived access token
For SSH-backed tools (
read_ha_file,list_ha_dir): SSH access to the HA host (e.g. via the Advanced SSH & Web Terminal add-on)For recorder DB tools (
query_recorder_db,get_logbook,get_zigbee_events): a connection string to the recorder database (MariaDB/MySQL)
Setup
1. Clone
git clone https://github.com/LoganInTX/home-assistant
cd home-assistantThe base hass-mcp code is vendored in the repo, so no submodule steps are needed.
2. Install dependencies
uv sync3. Configure credentials
Copy .env.example to .env and fill in your values:
cp .env.example .envHA_URL=http://homeassistant.local:8123
HA_TOKEN=YOUR_LONG_LIVED_ACCESS_TOKENGenerate a long-lived access token in Home Assistant under Profile > Long-Lived Access Tokens.
SSH access (optional)
read_ha_file and list_ha_dir shell out to ssh. To use them:
Generate a dedicated key:
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_hassAdd the public key to the Advanced SSH add-on's
authorized_keyssettingSet the optional env vars in
.envif your host differs from the defaults:
HA_SSH_USER=hassio
HA_SSH_HOST=homeassistant.local
HA_SSH_KEY=~/.ssh/id_ed25519_hassSSH reads are restricted to /config, /share, /ssl, /addons, /media,
and /backup.
Recorder database access (optional)
query_recorder_db, get_logbook, and get_zigbee_events connect directly to
the recorder database. Set HA_DB_URL in .env with the recorder's connection
string (find it under your recorder integration's db_url):
HA_DB_URL=mysql://homeassistant:YOUR_DB_PASSWORD@core-mariadb/homeassistant?charset=utf8mb44. Wire into Claude Code
The repo ships a .mcp.json that registers the server automatically when you
open the project in Claude Code. No additional configuration needed.
To use it in a different MCP client, run the server over stdio:
uv run python -m mcp_serverAlso included: add_scenes_view.py
A standalone script that pushes a Scenes view to the Home Assistant Overview
dashboard via WebSocket. Edit the SCENES_VIEW dict at the top of the file,
then run:
uv run python add_scenes_view.pyThe script connects via WebSocket, finds the existing Scenes view (or appends
one), and saves the updated config back to HA. It reads HA_URL and HA_TOKEN
from .env.
Adding a new tool
Add an async def decorated with @mcp.tool() to mcp_server/extensions.py.
The docstring becomes the tool description shown to MCP clients.
@mcp.tool()
async def my_tool(arg: str) -> str:
"""One-line description shown to the client.
Args:
arg: What this argument does.
"""
...Updating the vendored hass-mcp
The base server under mcp_server/vendor/app/ is a vendored copy of
voska/hass-mcp (currently v0.4.1). It is no
longer a submodule, so newer upstream features must be ported in manually:
# Pull the version you want from upstream, then copy its app/ over the vendored copy
git clone --depth 1 --branch <tag> https://github.com/voska/hass-mcp /tmp/hass-mcp
rsync -a --exclude='__pycache__' /tmp/hass-mcp/app mcp_server/vendor/
uv run python -m mcp_server # smoke testUpdate the version recorded in mcp_server/vendor/NOTICE.md when you do, and
re-apply any local modifications to the vendored code.
Security
Never commit
.env— it holds your token and DB password, and is gitignored.SSH access uses a dedicated key with
IdentitiesOnly=yesandBatchMode=yes.File reads over SSH are path-validated against an allowlist before the remote command is issued.
License
This project is licensed under the MIT License — see LICENSE
(© 2026 Logan Boyd).
The vendored base server under mcp_server/vendor/app/ is from
voska/hass-mcp, also MIT-licensed
(© 2025 Matt Voska) — see mcp_server/vendor/LICENSE
and mcp_server/vendor/NOTICE.md.
Available Tools
28 toolscall_service_toolA
Call any Home Assistant service (low-level API access)
Args: domain: The domain of the service (e.g., 'light', 'switch', 'automation') service: The service to call (e.g., 'turn_on', 'turn_off', 'toggle') data: Optional data to pass to the service (e.g., {'entity_id': 'light.living_room'})
Returns: A dictionary with success status, the domain/service called, and the list of affected entity states returned by Home Assistant.
Examples: domain='light', service='turn_on', data={'entity_id': 'light.x', 'brightness': 255} domain='automation', service='reload' domain='fan', service='set_percentage', data={'entity_id': 'fan.x', 'percentage': 50}
Inputs: domain (required), service (required), data. Returns: result
create_or_update_automationA
Create or overwrite a stored automation config via the HA REST API.
Args:
automation_id: Unique string ID for the automation (e.g. a timestamp like
"1716300000000"). Use a new value to create; pass the existing ID
to update in place.
config: Full automation config dict. Must include at least alias,
trigger, action, and mode. Do NOT include "id" — it is
injected automatically from automation_id.
Returns the raw HA response (usually {"result": "ok"}).
Inputs: automation_id (required), config (required).
domain_summary_toolA
Get a summary of entities in a specific domain
Args: domain: The domain to summarize (e.g., 'light', 'switch', 'sensor') example_limit: Maximum number of examples to include for each state
Returns: A dictionary containing: - total_count: Number of entities in the domain - state_distribution: Count of entities in each state - examples: Sample entities for each state - common_attributes: Most frequently occurring attributes
Examples: domain="light" - get light summary domain="climate", example_limit=5 - climate summary with more examples Best Practices: - Use this before retrieving all entities in a domain to understand what's available
Inputs: domain (required), example_limit. Returns: result
entity_actionA
Perform an action on a Home Assistant entity (on, off, toggle)
Args: entity_id: The entity ID to control (e.g. 'light.living_room') action: The action to perform ('on', 'off', 'toggle') params: Optional dictionary of additional parameters for the service call
Returns: The response from Home Assistant
Examples: entity_id="light.living_room", action="on", params={"brightness": 255} entity_id="switch.garden_lights", action="off" entity_id="climate.living_room", action="on", params={"temperature": 22.5}
Domain-Specific Parameters: - Lights: brightness (0-255), color_temp, rgb_color, transition, effect - Covers: position (0-100), tilt_position - Climate: temperature, target_temp_high, target_temp_low, hvac_mode - Media players: source, volume_level (0-1)
Inputs: entity_id (required), action (required), params.
get_addon_logsA
Fetch logs for a Supervisor add-on as plain text.
Args:
slug: Add-on slug (e.g. "a0d7b954_nginxproxymanager").
tail: If set, return only the last N lines.
Inputs: slug (required), tail. Returns: result
get_core_logA
Fetch the Home Assistant Core log as plain text.
Replaces the upstream get_error_log tool, which hits /api/error_log —
an endpoint removed in HA 2026.5+. This uses the Supervisor proxy
/api/hassio/core/logs/latest instead.
Args:
tail: If set, return only the last N lines after any grep filtering.
grep: Optional case-insensitive substring filter applied per line
(e.g. "http.ban" to find failed login attempts).
Inputs: tail, grep. Returns: result
get_device_infoA
Look up device registry info for a device — integration, manufacturer, model, connections.
Useful for determining which network/protocol a device uses (Zigbee, Z-Wave, Matter, Thread, etc.) and its config entry, so you know how to re-pair or debug it.
Pass exactly one of entity_id or device_name.
Args:
entity_id: Any entity belonging to the device, e.g.
"binary_sensor.motion_pantry_occupancy".
device_name: Friendly device name, e.g. "Motion Pantry".
Matched case-insensitively against the device's name and
name_by_user fields.
Returns a dict with keys: id, name, name_by_user,
manufacturer, model, sw_version, hw_version,
integration (config entry domain), config_entry_title,
connections (e.g. Zigbee IEEE address), identifiers,
and disabled_by.
Inputs: entity_id, device_name.
get_entities_by_areaA
Get all entities assigned to a specific Home Assistant area (room).
Area lookup is case-insensitive and matches the area's name as configured in Home Assistant (e.g., "Kitchen", "Living Room"). Entities inherit their area from their parent device when no area is set directly, matching HA's own resolution behavior.
Args: area: Name of the area to filter by (case-insensitive) domain: Optional domain to further filter results (e.g., 'light') lean: If True (default), returns token-efficient entity records
Returns: A dictionary containing: - area: The matched area name (as canonicalized by HA) - count: Number of matching entities - entities: List of entity records with their state and area
Examples: get_entities_by_area(area="Kitchen") - everything in the kitchen get_entities_by_area(area="Living Room", domain="light") - lights only
Inputs: area (required), domain, lean. Returns: result
get_entityA
Get the state of a Home Assistant entity with optional field filtering
Args: entity_id: The entity ID to get (e.g. 'light.living_room') fields: Optional list of fields to include (e.g. ['state', 'attr.brightness']) detailed: If True, returns all entity fields without filtering
Examples: entity_id="light.living_room" - basic state check entity_id="light.living_room", fields=["state", "attr.brightness"] - specific fields entity_id="light.living_room", detailed=True - all details
Inputs: entity_id (required), fields, detailed.
get_error_logA
Get the Home Assistant error log for troubleshooting
Returns: A dictionary containing: - log_text: The full error log text - error_count: Number of ERROR entries found - warning_count: Number of WARNING entries found - integration_mentions: Map of integration names to mention counts - error: Error message if retrieval failed
Examples: Returns errors, warnings count and integration mentions Best Practices: - Use this tool when troubleshooting specific Home Assistant errors - Look for patterns in repeated errors - Pay attention to timestamps to correlate errors with events - Focus on integrations with many mentions in the log
Inputs: no named parameters. Returns: result
get_ha_config_itemA
Get a stored HA config item (automation, scene, script, etc.) by numeric ID.
Args:
item_type: One of automation, scene, script.
item_id: Numeric config ID (not the entity_id) — e.g. "1722562028372".
Inputs: item_type (required), item_id (required).
get_historyA
Get the history of an entity's state changes
Args: entity_id: The entity ID to get history for hours: Number of hours of history to retrieve (default: 24)
Returns: A dictionary containing: - entity_id: The entity ID requested - states: List of state objects with timestamps - count: Number of state changes found - first_changed: Timestamp of earliest state change - last_changed: Timestamp of most recent state change
Examples: entity_id="light.living_room" - get 24h history entity_id="sensor.temperature", hours=168 - get 7 day history Best Practices: - Keep hours reasonable (24-72) for token efficiency - Use for entities with discrete state changes rather than continuously changing sensors - Consider the state distribution rather than every individual state
Inputs: entity_id (required), hours. Returns: result
get_logbookA
Fetch logbook entries for an entity, including full event attributes.
Queries the recorder MariaDB directly via SSH rather than the logbook HTTP API, avoiding the massive response payloads the API endpoint returns.
For event.*_action entities the action name is extracted from
state_attributes.shared_attrs and returned as action. For all
other entities state is returned as-is.
Args:
entity_id: Entity to query (e.g. "event.master_light_switch_action").
hours: How many hours of history to fetch (default 24, max 168).
Inputs: entity_id (required), hours. Returns: result
get_lovelace_dashboardA
Fetch a full Lovelace dashboard config via WebSocket.
Args:
url_path: Dashboard slug (e.g. "scenes" for a custom dashboard).
Pass None (default) for the built-in Overview dashboard.
Inputs: url_path.
get_lovelace_viewA
Fetch a single view from a Lovelace dashboard by title or path.
Args:
view_name: Matched against view['path'] first, then view['title']
(case-insensitive).
url_path: Dashboard slug, or None for the Overview dashboard.
Inputs: view_name (required), url_path.
get_versionA
Get the Home Assistant version
Returns: A string with the Home Assistant version (e.g., "2025.3.0")
Inputs: no named parameters. Returns: result
get_zigbee_eventsA
Fetch recent Zigbee action events for a device directly from the recorder DB.
Much more efficient than the logbook API — returns only the rows you need without loading the entire logbook into memory.
The action is stored in state_attributes.shared_attrs as event_type
(e.g. down_double, up_single). This tool extracts it via
JSON_UNQUOTE and returns one row per event.
Args:
device_name: Friendly device name as it appears in Zigbee2MQTT, e.g.
"Master Light Switch". Converted to the entity slug automatically
(e.g. event.master_light_switch_action).
minutes: How many minutes of history to return (default 60, max 10080).
action: Optional filter, e.g. "down_double". If omitted, all
actions are returned.
Inputs: device_name (required), minutes, action. Returns: result
list_automationsA
Get a list of all automations from Home Assistant
This function retrieves all automations configured in Home Assistant, including their IDs, entity IDs, state, and display names.
Returns: A list of automation dictionaries, each containing id, entity_id, state, and alias (friendly name) fields.
Examples: Returns all automation objects with state and friendly names
Inputs: no named parameters. Returns: result
list_entitiesA
Get a list of Home Assistant entities with optional filtering
Args: domain: Optional domain to filter by (e.g., 'light', 'switch', 'sensor') search_query: Optional search term to filter entities by name, id, or attributes (Note: Does not support wildcards. To get all entities, leave this empty) limit: Maximum number of entities to return (default: 100) fields: Optional list of specific fields to include in each entity detailed: If True, returns all entity fields without filtering
Returns: A list of entity dictionaries with lean formatting by default
Examples: domain="light" - get all lights search_query="kitchen", limit=20 - search entities domain="sensor", detailed=True - full sensor details
Best Practices: - Use lean format (default) for most operations - Prefer domain filtering over no filtering - For domain overviews, use domain_summary_tool instead of list_entities - Only request detailed=True when necessary for full attribute inspection - To get all entity types/domains, use list_entities without a domain filter, then extract domains from entity_ids
Inputs: domain, search_query, limit, fields, +1 more. Returns: result
list_ha_dirA
List a directory on the Home Assistant host over SSH (ls -la).
Restricted to the same allowed roots as :func:read_ha_file.
Args:
path: Absolute directory path (e.g. /config).
Inputs: path (required). Returns: result
query_recorder_dbA
Run a read-only SQL SELECT against the Home Assistant recorder (MariaDB).
Use this for ad-hoc queries against the recorder database — e.g. fetching Zigbee action events, checking state history, or inspecting statistics.
Only SELECT statements are allowed. Key tables:
states— entity state changes (joined tostates_metafor entity_id)states_meta— mapsmetadata_id->entity_idevents— HA events (joined toevent_typesfor event name)event_types— mapsevent_type_id->event_typeevent_data— JSON payload blobs (joined viadata_id)statistics— hourly aggregatesstatistics_short_term— 5-minute aggregates
Timestamps are stored as DATETIME in UTC (last_updated_ts / time_fired_ts
are FLOAT unix epoch seconds; the older last_updated / time_fired DATETIME
columns may be NULL in recent HA versions — use the _ts columns instead).
Args: sql: A SELECT statement to execute.
Inputs: sql (required). Returns: result
read_ha_fileA
Read a file from the Home Assistant host over SSH.
Reads are restricted to standard HA-exposed directories: /config,
/share, /ssl, /addons, /media, /backup.
Args:
path: Absolute path on the HA host (e.g. /config/ip_bans.yaml).
tail: If set, return only the last N lines after any grep filtering.
grep: Optional case-insensitive substring filter applied per line.
Inputs: path (required), tail, grep. Returns: result
restart_haA
Restart Home Assistant
⚠️ WARNING: Temporarily disrupts all Home Assistant operations
Returns: Result of restart operation
Inputs: no named parameters. Returns: result
save_lovelace_dashboardA
Save (overwrite) an entire Lovelace dashboard config.
Args:
config: The complete dashboard config dict (as returned by
:func:get_lovelace_dashboard).
url_path: Dashboard slug, or None for Overview.
Returns the raw HA response envelope so the caller can inspect success.
Inputs: config (required), url_path.
search_addon_logsA
Fetch add-on logs and filter lines by a case-insensitive substring.
Avoids blowing the token limit when logs are very large.
Args:
slug: Add-on slug (e.g. "45df7312_zigbee2mqtt").
grep: Case-insensitive substring to filter lines by.
tail: Return only the last N matching lines (default 200).
Inputs: slug (required), grep (required), tail. Returns: result
search_entities_toolA
Search for entities matching a query string
Args: query: The search query to match against entity IDs, names, and attributes. (Note: Does not support wildcards. To get all entities, leave this blank or use list_entities tool) limit: Maximum number of results to return (default: 20)
Returns: A dictionary containing search results and metadata: - count: Total number of matching entities found - results: List of matching entities with essential information - domains: Map of domains with counts (e.g. {"light": 3, "sensor": 2})
Examples: query="temperature" - find temperature entities query="living room", limit=10 - find living room entities query="", limit=500 - list all entity types
Inputs: query (required), limit. Returns: result
system_overviewA
Get a comprehensive overview of the entire Home Assistant system
Returns: A dictionary containing: - total_entities: Total count of all entities - domains: Dictionary of domains with their entity counts and state distributions - domain_samples: Representative sample entities for each domain (2-3 per domain) - domain_attributes: Common attributes for each domain - area_distribution: Entities grouped by area (if available)
Examples: Returns domain counts, sample entities, and common attributes Best Practices: - Use this as the first call when exploring an unfamiliar Home Assistant instance - Perfect for building context about the structure of the smart home - After getting an overview, use domain_summary_tool to dig deeper into specific domains
Inputs: no named parameters. Returns: result
update_stored_sceneA
Update a stored scene's entity states.
Args:
scene_id: Numeric scene ID from get_ha_config_item("scene", ...).
name: Display name for the scene.
entities: Mapping of entity_id -> state dict, e.g.
{"fan.upstairs": {"state": "on", "percentage": 16}}.
Inputs: scene_id (required), name (required), entities (required).
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 28 tools
Several tools overlap in purpose (e.g., get_error_log vs get_core_log, call_service_tool vs entity_action, list_entities vs search_entities_tool), though descriptions help differentiate. Still, the boundaries are not always clear for an agent.
Most tools follow a verb_noun pattern with snake_case, but some use noun_verb (e.g., entity_action) or have inconsistent suffixes like '_tool'. The pattern is recognizable but not fully consistent.
28 tools is on the higher side for a single server, but given the breadth of Home Assistant, it is justifiable. Some redundancy suggests it could be streamlined.
Core operations are covered (read entities, call services, manage automations/scenes, logs), but missing delete operations for automations/scenes and no tool to create a scene from scratch. Gaps exist but are not critical.
Related MCP Connectors
A TypeScript MCP server for Home Assistant, enabling programmatic management of entities, automati…
An MCP server that let you interact with Cycloid.io Internal Development Portal and Platform
MCP server for OpenAI API (chat completions, image generation, embeddings) via AceDataCloud
Related MCP Servers
- AlicenseNot gradedqualityBmaintenanceAn MCP server and Home Assistant add-on that enables AI assistants to manage smart homes by creating automations, designing dashboards, and interacting with entities. It features native access to Home Assistant APIs, built-in Git versioning for safe rollbacks, and full management of HACS integrations.655MIT
- AlicenseAqualityDmaintenanceEnhanced Home Assistant MCP server with 22 context efficient tools for smart home control, automation trace debugging, entity registry management, CEL expression queries, and long-term statistics.2217 PyPI3MIT
- AlicenseAqualityDmaintenanceMCP server for controlling and querying Home Assistant via its REST API, exposing tools to get entity states, list all states, and call services.16154 npmMIT
- AlicenseBqualityDmaintenanceMCP server for full Home Assistant control, enabling AI agents to manage dashboards, automations, files, apps, entities, and more via REST API, WebSocket, and SSH.66182 npmMIT