Skip to main content
Glama
hass-mcp-extensions

hass-mcp-extensions

by LoganInTX

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

get_lovelace_dashboard

Fetch a full Lovelace dashboard config via WebSocket

get_lovelace_view

Fetch a single view from a dashboard by title or path

save_lovelace_dashboard

Overwrite an entire Lovelace dashboard config

get_ha_config_item

Get a stored automation, scene, or script by numeric ID

get_addon_logs

Fetch Supervisor add-on logs as plain text

get_core_log

Fetch the HA Core log (replaces the removed /api/error_log endpoint)

search_addon_logs

Grep a Supervisor add-on's logs for a pattern

read_ha_file

Read a file from the HA host over SSH

list_ha_dir

List a directory on the HA host over SSH

create_or_update_automation

Create or overwrite a stored automation via the REST API

update_stored_scene

Update a stored scene's entity states

get_device_info

Resolve a device's entities, area, and metadata by entity ID or name

query_recorder_db

Run a read-only SQL query against the recorder (MariaDB) database

get_logbook

Fetch logbook entries from the recorder database

get_zigbee_events

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+

  • uv

  • 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-assistant

The base hass-mcp code is vendored in the repo, so no submodule steps are needed.

2. Install dependencies

uv sync

3. Configure credentials

Copy .env.example to .env and fill in your values:

cp .env.example .env
HA_URL=http://homeassistant.local:8123
HA_TOKEN=YOUR_LONG_LIVED_ACCESS_TOKEN

Generate 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:

  1. Generate a dedicated key: ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_hass

  2. Add the public key to the Advanced SSH add-on's authorized_keys setting

  3. Set the optional env vars in .env if your host differs from the defaults:

HA_SSH_USER=hassio
HA_SSH_HOST=homeassistant.local
HA_SSH_KEY=~/.ssh/id_ed25519_hass

SSH 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=utf8mb4

4. 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_server

Also 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.py

The 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 test

Update 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=yes and BatchMode=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 tools
call_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 to states_meta for entity_id)

  • states_meta — maps metadata_id -> entity_id

  • events — HA events (joined to event_types for event name)

  • event_types — maps event_type_id -> event_type

  • event_data — JSON payload blobs (joined via data_id)

  • statistics — hourly aggregates

  • statistics_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

    A3.6/5.0

    Scored across 28 tools

    Disambiguation3/5

    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.

    Naming Consistency3/5

    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.

    Tool Count3/5

    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.

    Completeness3/5

    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

    Related MCP Servers

    • A
      license
      Not graded
      quality
      B
      maintenance
      An 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.
      655
      MIT
    • A
      license
      A
      quality
      D
      maintenance
      Enhanced 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.
      22
      17 PyPI
      3
      MIT
    • A
      license
      A
      quality
      D
      maintenance
      MCP server for controlling and querying Home Assistant via its REST API, exposing tools to get entity states, list all states, and call services.
      16
      154 npm
      MIT
    • A
      license
      B
      quality
      D
      maintenance
      MCP server for full Home Assistant control, enabling AI agents to manage dashboards, automations, files, apps, entities, and more via REST API, WebSocket, and SSH.
      66
      182 npm
      MIT