Letting an AI Assistant Touch My Smart Home
Home Assistant speaks MCP. Here is how I connected an AI agent to around fifty devices, and why I ended up giving it far fewer tools than it wanted.
Letting an AI Assistant Touch My Smart Home
My Home Assistant install has been running locally for a while now. Over
fifty devices: lights, thermometers, window sensors, a vacuum, a few
self-built presence detectors. Voice assistants handle the easy half of
that fine. "Turn off the kitchen light" has been a solved problem for
years.
The other half was never solved. Questions like why did the heating run
at three in the morning need something that can read a lot of state at
once, correlate it, and then go edit a YAML file. That is a different
kind of tool.
Home Assistant ships an integration that makes this possible, and almost
nobody talks about it.
What MCP actually is here
MCP is a protocol for handing an AI client a list of tools it may call.
The client asks the server "what can you do", gets back a list of
functions with typed arguments, and can then call them.
Home Assistant implements the server side. It takes your existing
voice-assistant intents — the same ones the built-in Assist pipeline uses
— and exposes them over HTTP. So the agent gets HassTurnOn,
HassLightSet, HassClimateSetTemperature, GetLiveContext and friends.
Nothing new is invented. It is the Assist layer with a different door.
Setup
1. Decide what it is allowed to see first
Do this before anything else. It is the actual security boundary, and it
is easy to skip because the integration works without touching it.
Go to Settings → Voice assistants → Expose. Everything on that page is
what any MCP client will be able to read and control. Everything not on
it is invisible.
I started from an empty list and added things back in deliberately.
Lights, climate, the vacuum, sensors I actually ask about. Not the door
locks. Not anything that costs money when triggered by accident.
2. Add the integration
Settings → Devices & services → Add Integration → Model Context
Protocol Server. There is no configuration to fill in — the entity
exposure from step one is the configuration.
3. Create a token
Profile → Security → Long-lived access tokens → Create token.
Copy it once, it is not shown again. Worth knowing: a long-lived token
inherits the permissions of the user that created it. If you create it
from your admin account, it is an admin token. I made a separate Home
Assistant user for this, non-admin, and generated the token from there.
4. Point the client at it
The endpoint is /api/mcp. For Claude Code:
Or as JSON, if you prefer editing config directly:
--scope user makes it available in every project. --scope project
writes it into a .mcp.json that gets committed — do not do that with a
token in it.
That is the whole setup. Under five minutes if you have already decided
what to expose.
The part that surprised me: tools are not free
Every tool the server offers gets described to the model on every single
request. Name, description, full argument schema. That text is part of
your context window before you have typed anything.
With a generous exposure list I was carrying a noticeable chunk of
context in tool definitions alone, on every message, in every project —
including the ones that had nothing to do with my apartment.
Two things fixed it:
Trim the exposure list. This is the lever that actually works,
because it is server-side. Fewer exposed entities means smaller tool
descriptions and fewer irrelevant options for the model to consider. It
also made the agent noticeably more accurate. Given six lights it picks
the right one. Given sixty it guesses.
Do not load it where you do not need it. A smart-home connector
sitting in a web project is pure overhead. Scope it deliberately.
What it is genuinely good at
Three things earn their keep:
Reading the whole picture at once. GetLiveContext returns current
state across everything exposed. Asking "which windows are open and what
is the heating doing" gets a real answer instead of three separate voice
queries.
Writing automations. This is the big one. Describing an automation in
a sentence and getting valid YAML back, with the correct entity IDs
because it can look them up, removes most of the friction from the job I
hate. I still read every line before it goes in.
Debugging after the fact. Correlating a sensor trace with an
automation trigger is tedious by hand and quick for something that can
read both.
What it is bad at
It is not a voice assistant. Round trips take seconds, which is fine for
"write me an automation" and useless for "turn off the light" while you
are walking past the switch. The built-in Assist pipeline is still the
right tool for that, and the two coexist happily.
It is also, structurally, a thing with a token that can turn stuff on in
your home. Treat the exposure list as the boundary it really is, keep the
token off any machine you do not control, and do not put it in a repo.
Would I do it again
Yes, but with the exposure list from the start rather than trimming it
back later. The setup is trivial. The thinking about what it may touch
is the actual work, and it is worth doing before you generate the token
rather than after.