MCP connects AI to the home, but it does not replace Home Assistant
Home Assistant's native MCP server lets Claude, Codex, or ChatGPT read live context and use Assist API tools for explicitly exposed entities. It helps inventory entity IDs, understand state, and test a command. It is not a general editor: do not assume it can write automations.yaml, modify a dashboard, or administer every integration.
The dependable flow is observe, propose, review, then save in Home Assistant. The final automation runs locally without calling AI on every trigger. Internet downtime or an AI plan limit must not stop a leak alert or room lighting.
| Need | Native MCP server | Recommended method |
|---|---|---|
| Read states and IDs | Yes, for exposed entities | Assist context |
| Control an entity | Yes, when enabled | Assist tool with approval |
| Generate YAML | The model can draft | Paste, review, validate |
| Persist an automation | Not a general admin function | HA editor or managed file |
| Modify a dashboard | Not a general admin function | UI editor or YAML dashboard |
| Use external tools from HA | Opposite direction | MCP client integration |
Choose the client and transport
Home Assistant exposes /api/mcp over Streamable HTTP. Codex supports stdio and Streamable HTTP; Claude Code recommends HTTP for remote servers. ChatGPT Web uses a remote MCP app or plugin and does not read local Codex configuration. Availability also depends on plan and workspace policy.
The server exposes Home Assistant to an AI client. The MCP client integration does the reverse: it lets a conversation agent in Home Assistant use an external MCP server.
| Client | Local | Remote | Main limit |
|---|---|---|---|
| Claude Desktop | stdio proxy to /api/mcp | HTTPS connector | Remote connector uses Anthropic cloud |
| Claude Code | HTTP and OAuth | HTTP and OAuth | Local, project, or user scope |
| Codex CLI/IDE | Streamable HTTP and OAuth | Streamable HTTP and OAuth | Global or project configuration |
| ChatGPT | No direct local Web connection | HTTPS MCP app | Plus, Pro, Business, Enterprise/Edu per HA docs |
1. Prepare Home Assistant with least privilege
The exposed-entities page is the key boundary. /api/mcp/assist explicitly selects Assist; another LLM API requires an administrator user.
- Create and test a backup.
- Inventory exact entity IDs and raw states in Developer tools > States.
- Add Model Context Protocol Server under Devices & services.
- Expose only required sensors and actuators to Assist.
- Use a separate non-admin account for /api/mcp/assist where possible.
- Initially exclude locks, garage doors, alarms, main valves, and sensitive scripts.
2. Configure Claude, Codex, or ChatGPT
On Home Assistant OS and Container, add the native server through the UI; no add-on is required. A local proxy normally runs on the client, or in an isolated container. Never put its token in an image, Git, or shared file.
Claude Desktop can use mcp-proxy with a long-lived token on the LAN. Claude Code and Codex can use OAuth directly against a local URL. ChatGPT needs a remotely reachable HTTPS URL: Home Assistant Cloud is simplest; a tunnel requires exact external URL and forwarded-header alignment.
mcp_oauth_callback_port = 12345
[mcp_servers.homeassistant]
url = "https://ha.example.net/api/mcp/assist"
auth = "oauth"
oauth = { client_id = "http://127.0.0.1:12345" }
# Then: codex mcp login homeassistantclaude mcp add-json "HA" '{
"type": "http",
"url": "https://ha.example.net/api/mcp/assist",
"oauth": {"clientId": "http://localhost:12345", "callbackPort": 12345}
}' --client-secret3. Protect OAuth, tokens, and remote access
Prefer OAuth. Home Assistant uses a Client ID Metadata Document rather than RFC 7591 dynamic registration. The Client ID is https://claude.ai for the remote Claude connector and https://chatgpt.com for ChatGPT; it is never the Home Assistant URL.
A long-lived token remains powerful until revoked. Store it in a secret manager or environment variable. With a tunnel, the hostname must exactly match the external URL and the proxy must forward the correct headers.
- Valid TLS, one canonical URL, and MFA.
- No secrets in prompts, YAML, or repositories.
- Human confirmation for sensitive writes.
- Logging and a tested revocation procedure.
4. Observe before allowing control
Start by listing exposed entities, reading one room, and explaining an automation. Then test one reversible command on a lamp. A text response does not prove a tool call: inspect the client log and Home Assistant state.
Read only the exposed Home Assistant context. List the entity IDs for presence, illuminance, energy, and temperature. Flag missing or unavailable entities. Do not control a device or modify configuration.Example 1 — lighting based on presence and illuminance
This draft runs locally after it is saved in Home Assistant. Replace the entity IDs and threshold after observing the real sensor. Triggers start evaluation; conditions then require both presence and low illuminance.
- id: office_presence_lux_lighting
alias: Office - lighting from presence and illuminance
triggers:
- trigger: state
entity_id: binary_sensor.office_presence
to: "on"
id: presence
- trigger: numeric_state
entity_id: sensor.office_illuminance
below: 80
id: dark
- trigger: state
entity_id: binary_sensor.office_presence
to: "off"
for: "00:05:00"
id: vacant
actions:
- choose:
- conditions:
- condition: template
value_template: "{{ trigger.id in ['presence', 'dark'] }}"
- condition: state
entity_id: binary_sensor.office_presence
state: "on"
- condition: numeric_state
entity_id: sensor.office_illuminance
below: 80
sequence:
- action: light.turn_on
target:
entity_id: light.office_ceiling
- conditions:
- condition: template
value_template: "{{ trigger.id == 'vacant' }}"
sequence:
- action: light.turn_off
target:
entity_id: light.office_ceiling
mode: restartExample 2 — water-leak alert without an irreversible action
A leak alert must keep working without AI. Automatic valve closure requires testing action direction, power, unavailable states, and manual override, so this example only alerts.
- id: basement_water_leak_alert
alias: Basement - water leak alert
triggers:
- trigger: state
entity_id: binary_sensor.basement_water_leak
to: "on"
actions:
- action: persistent_notification.create
data:
title: "Water leak detected"
message: "Check the basement sensor immediately."
- action: notify.mobile_app_marc_phone
data:
title: "Home Assistant alert"
message: "Water leak detected in the basement."
mode: singleReview this draft without applying it. Confirm raw states, the notify service, unavailable handling, and the expected trace. Do not close a valve or disable an alarm.Example 3 — energy, temperature, and presence dashboard
A dashboard created in the UI is normally stored by Home Assistant; a YAML dashboard remains a separately managed file. Do not overwrite internal storage with an external tool. Start with native cards before adding a HACS dependency.
title: Home
views:
- title: Overview
path: home
cards:
- type: entities
title: Presence
entities:
- binary_sensor.office_presence
- person.marc
- type: gauge
entity: sensor.home_power_w
name: Current power
min: 0
max: 10000
severity:
green: 0
yellow: 4000
red: 7000
- type: history-graph
title: Temperature - 24 h
hours_to_show: 24
entities:
- sensor.living_room_temperature
- sensor.basement_temperature8. Save, validate, and test
YAML traces require an id and Home Assistant keeps five by default. numeric_state reacts to crossing a threshold, not merely being below it.
- Paste through Edit in YAML, save, and reopen the automation.
- Keep a unique id; automations.yaml must remain a list and must not be edited simultaneously by UI and hand.
- Run configuration validation under Developer tools > YAML.
- Test conditions and actions, then cause a real state change.
- Read the trace: trigger, conditions, choose branch, service, and error.
- Watch unavailable states, duplicate triggers, and execution mode.
9. Rollback and governance
A community MCP server with direct writes or many admin tools increases risk. Verify its primary repository, version, advertised tools, read/write annotations, and authentication; isolate it and keep human approval. MCP is not a security guarantee.
- Name the backup and record the Home Assistant version.
- Version manually managed files without secrets.
- Disable before deleting and preserve the error trace.
- Revoke OAuth or the token, remove the MCP app, and shrink exposed entities.
- Document who approves sensitive actions.
Quick troubleshooting
| Symptom | Likely cause | Check |
|---|---|---|
| 404 at /api/mcp | Missing integration or wrong path | Add integration and use /api/mcp/assist |
| 401 Unauthorized | Wrong token | Reauthenticate and revoke old secret |
| OAuth loop | Client ID or URL mismatch | Check Client ID and callback |
| Tunnel rejection | Issuer or proxy headers | Align external URL and X-Forwarded-* |
| Missing entity | Not exposed | Check Expose and exact entity ID |
| YAML has no effect | Condition or raw state | Read trace and states |
| Broken dashboard | Missing HACS card | Return to native cards |
A reliable integration stays local at runtime
Claude, Codex, and ChatGPT accelerate inventory, drafting, and review when MCP gives real context. Quality comes from boundaries: minimal entities, suitable authentication, drafts instead of blind writes, Home Assistant validation, and tested rollback.
This guide offers a method and fictitious examples; it does not replace a security and reliability review of a complex installation.