Skip to content
Go back

Home Assistant MCP: Server and Client

By KingPin 12 min read
Home Assistant MCP: Server and Client
Contents

Two Integrations, Three Letters, Almost Nothing in Common

Home Assistant added Model Context Protocol support back in 2025.2, and ever since, people have been mixing up two integrations that share three letters and do almost nothing alike. One turns your house into an MCP server that Claude and ChatGPT can talk to. The other turns Home Assistant into an MCP client that fetches tools from somewhere else and hands them to its own conversation agent.

The verdict up front: the server side (mcp_server) is usable today. Set it up once, curate what you expose, and you have a real assistant that can flip lights and read sensor state from Claude Code or Claude Desktop. The client side (mcp) is much narrower than the name suggests. Tools only, polling only, SSE only, no stdio. It’s worth setting up if you already run a conversation agent in Home Assistant and want it talking to an external tool server. If you don’t already have that agent running, skip it.

Everything below is checked against Home Assistant 2026.9.3 (released 2026-09-18), the current release as of September 2026. Both integrations sit at quality scale silver, both are maintained by the same codeowner, and both landed in the same release. That’s basically where the similarities end.

The Server: Your House Answers Questions

mcp_server exposes a Streamable HTTP endpoint at /api/mcp. It’s stateless, so every request carries its own auth and there’s no session to babysit. You can also target a specific LLM API by ID with /api/mcp/<api_id>, and the built-in Assist API always lives at /api/mcp/assist. Ask for an API ID that doesn’t exist and you get a plain 404, no guessing.

There’s a legacy SSE transport too, at /mcp_server/sse plus /mcp_server/messages/{session_id}, left in for older clients. New setups should use Streamable HTTP.

The manifest depends on http and conversation, pins mcp==1.28.1, and enforces single_config_entry: true. You get exactly one config entry for this integration. No running two separate MCP servers with different scopes side by side. If you want different exposure levels for different clients, you’re managing that through the Assist exposed-entities list, not through multiple entries.

Auth and the OAuth Trap Everyone Hits

Home Assistant supports two auth paths for mcp_server: OAuth, or a long-lived access token you generate from your user profile’s Security tab. The token path is simpler and it’s the one to use for mcp-proxy or Codex. The OAuth path is where people burn an evening.

Home Assistant implements OAuth through IndieAuth. That means the Client ID in the OAuth flow is the base URL of the client application itself: https://claude.ai for Claude, https://chatgpt.com for ChatGPT. Not your Home Assistant URL. Home Assistant checks that the OAuth redirect_uri shares scheme and domain with the client_id, and if you type your own instance URL into that field, the whole handshake fails silently or with an error that doesn’t point at the real problem. The Client Secret field is unused by Home Assistant. If a client’s form insists on one, type anything.

Home Assistant supports the OAuth Client ID Metadata Document spec but does not run an RFC 7591 dynamic registration endpoint. A client that requires strict RFC 7591 registration can’t register against your instance at all. Check what your MCP client expects before you spend an hour blaming your reverse proxy.

Speaking of reverse proxies: the hostname your remote client connects through has to match whatever you’ve set as the Internal URL or External URL in Settings, System, Network. If it doesn’t match, Home Assistant can’t resolve the issuer field correctly in /.well-known/oauth-authorization-server, starts handing back relative paths, and any spec-compliant OAuth client rejects the metadata outright. This is a common reason “it works from my laptop but not from Claude’s cloud” happens.

Local Setups: Skip the Cloud Entirely

If you’re not interested in exposing Home Assistant to the internet, run mcp-proxy on the same machine as your MCP client:

Terminal window
uv tool install git+https://github.com/sparfenyuk/mcp-proxy

Then point Claude Desktop at your local instance with a long-lived token, no OAuth dance required:

claude_desktop_config.json
{
"mcpServers": {
"Home Assistant": {
"command": "mcp-proxy",
"args": [
"--transport=streamablehttp",
"--stateless",
"http://<your_local_home_assistant_ip_or_url>:8123/api/mcp"
],
"env": {
"API_ACCESS_TOKEN": "<your_access_token_here>"
}
}
}
}

This is the setup to use if you keep Home Assistant on a home network and have no interest in remote access. No public exposure, no reverse proxy, no OAuth metadata to get right. Generate a token, drop it in, done.

Claude Code and Codex Get Real OAuth Support

If your Home Assistant is reachable and you want to add it as a tool source for Claude Code, the CLI can do the OAuth flow itself:

Terminal window
claude mcp add-json "HA" '{
"type": "http",
"url": "https://<your_home_assistant_url>/api/mcp",
"oauth": {
"clientId": "http://localhost:12345",
"callbackPort": 12345
}
}' --client-secret

That clientId of http://localhost:12345 looks wrong at first glance if you just read the IndieAuth explanation above, but it’s correct here. It’s the address of Claude Code’s own local callback server on your machine, not your Home Assistant URL. The client ID has to describe the OAuth client, and in this case the OAuth client is the CLI process listening on your loopback interface.

Codex works the same way through ~/.codex/config.toml:

~/.codex/config.toml
mcp_oauth_callback_port = 12345
[mcp_servers.homeassistant]
url = "<your_home_assistant_url>/api/mcp"
auth = "oauth"
oauth = { client_id = "http://127.0.0.1:12345" }

Run codex mcp login homeassistant after saving that, and make sure the callback port in mcp_oauth_callback_port matches the port baked into client_id. Mismatch those two numbers and the browser redirect lands nowhere useful.

Once connected, Claude asks for permission before calling any tool. It doesn’t just start flipping switches because you asked a vague question. That’s a deliberate design choice worth keeping.

What You Actually Get: Tools, Prompts, One Resource

The server side supports prompts and tools fully. Resources work too, but only through the Assist API, and only one resource exists: homeassistant://assist/context-snapshot, named assist_context_snapshot. It’s plain text and it only shows up if your configured LLM API exposes the homeassistant__GetLiveContext tool. Its content matches what that tool returns, so it’s a convenience wrapper more than a separate feature.

Sampling and notifications are both unsupported. If an MCP client expects the server to initiate a request back to the model, or push a notification when something changes in Home Assistant, that path doesn’t exist. You’re in a request-response world: the client asks, the server answers.

Connecting to any LLM API other than the built-in Assist API requires the authenticated user to be an administrator. Assist itself stays open to non-admin users, which makes sense: Assist is the same intent layer that powers voice commands, and it’s already scoped down by the exposed-entities list.

Nabu Casa or Fight Your Router

Remote connections from Claude’s cloud connector or ChatGPT need your Home Assistant instance to be publicly reachable, because the connection is brokered through the vendor’s own cloud infrastructure. There’s no way around that for the hosted clients; they aren’t running on your network. Home Assistant Cloud through Nabu Casa (https://<id>.ui.nabu.casa) is the documented path for this and it sidesteps certificate and reverse proxy configuration you’d otherwise have to get exactly right. If you already pay for Nabu Casa for the remote UI access, this is one more reason it earns its keep. If you don’t, and you don’t want to run one, use mcp-proxy locally instead and skip the whole remote story.

The Client: Home Assistant Goes Shopping for Tools

The mcp integration flips the relationship. Home Assistant becomes the client, and it polls an external MCP server’s SSE endpoint to list available tools. Setup asks for three things: the SSE Server URL, an OAuth Client ID, and an OAuth Client Secret.

The iot_class here is local_polling, versus local_push for the server integration. That distinction matters more than it looks: the client integration checks in periodically rather than holding an open stream, so tool availability updates on a poll cycle, not instantly.

Feature support on this side is thin by comparison. Tools are supported. Prompts, resources, sampling, and notifications are not. If you’re picturing a full symmetric MCP client that can pull resources or receive prompts from a remote server, adjust expectations. You get a tool list and nothing else.

SSE Only Means a Proxy for Almost Everything

The bigger catch: this integration only speaks SSE. Most MCP servers you’ll find in the wild are built for stdio, meant to run as a local subprocess your client launches directly. If the tool server you want to connect only speaks stdio, you need something like mcp-proxy running in front of it to expose an SSE endpoint Home Assistant can poll. That’s an extra moving part for every server you want to add, and it’s the reason this integration reads narrower in practice than the settings screen suggests.

Adding the Integration Doesn’t Do Anything by Itself

Easy to miss: adding the mcp integration and connecting to a working SSE server gets you exactly nothing until you also configure a conversation agent to use the new tools. Home Assistant’s Anthropic, Google Generative AI, Ollama, and OpenAI conversation integrations all support this, in the same way they already support Assist API tools. Go set that up as a second step, or you’ll be sitting there wondering why your remote tools never get called.

When It’s Worth Setting Up

If you’re already running one of those conversation agent integrations inside Home Assistant, whether that’s a local Ollama model or a cloud API, and you have a specific external tool server you want that agent to reach, this integration does exactly that job. If you don’t have a conversation agent configured yet, this isn’t the reason to start. Set up mcp_server first, get comfortable with the exposure model, and come back to the client side once you have an actual conversation agent that needs external tools.

The Exposure Model Is the Whole Security Story

Both integrations funnel through the same gate: whatever you’ve exposed to Assist on the Voice Assistants exposure page in Settings is what an MCP client can see or touch. Nothing more. That page is the entire access control model for both directions of MCP in Home Assistant.

Behind the scenes, exposure maps to intent handlers with names like HassTurnOn, HassTurnOff, HassToggle, HassGetState, HassSetPosition, HassClimateGetTemperature, HassStartTimer, HassCancelTimer, HassTimerStatus, HassBroadcast, HassGetCurrentTime, HassGetCurrentDate, HassRespond, and HassNevermind, plus HassStopMoving and the live-state snapshot tool homeassistant__GetLiveContext. An agent connected through MCP gets access to exactly the intents that apply to whatever you’ve exposed, and nothing about a locked door or an armed alarm panel unless you’ve put it on that list yourself.

Curate that list like you mean it. A remote client brokered through a vendor’s cloud is a reasonable way to ask an assistant to dim the lights. It’s a much worse way to leave your garage door or your alarm panel one confused prompt away from an unwanted action. Expose lights, climate, sensors, media players. Think twice about locks and anything that opens a physical barrier.

The Actual Verdict

mcp_server earns a weekend. The setup has real gotchas (the IndieAuth client ID confusion above all), but once you’re through them, you have a working, permission-gated bridge between real LLM clients and your house. mcp-proxy for local use cuts the whole thing down to a token and a config file, which is why I’d point almost anyone running Home Assistant with Claude Code or Claude Desktop at it.

mcp is a solid feature for a specific audience: people already running a conversation agent inside Home Assistant who want to extend it with an outside tool server. It’s not a general MCP client in the sense of pulling in resources or prompts from anywhere. It’s a tool bridge, SSE only, polling only. Know that going in and you won’t be disappointed by what it can’t do.

Common Questions

Do I need Nabu Casa to use Home Assistant’s MCP server?

No. Nabu Casa (Home Assistant Cloud) is the documented path for remote clients like Claude’s cloud connector or ChatGPT, which need a publicly reachable instance. For local use, run mcp-proxy on the same machine as your MCP client and connect over your local network with a long-lived access token. No public exposure required.

Can an MCP client see entities I haven’t exposed to Assist?

No. Both the mcp_server and mcp integrations route through the same exposure list under Settings, Voice Assistants. Whatever isn’t exposed to Assist isn’t visible or controllable through MCP, regardless of which client connects or how it authenticates. Curate that list before connecting anything.

What’s the difference between the mcp and mcp_server integrations?

mcp_server makes Home Assistant an MCP server that outside clients like Claude and ChatGPT connect to for tools and prompts. mcp makes Home Assistant an MCP client that polls an external SSE server for tools and hands them to its own conversation agent. They run independently and solve opposite problems.

Can a local-only Home Assistant instance work with Claude Desktop?

Yes. Install mcp-proxy on the machine running Claude Desktop, point it at your local instance’s /api/mcp endpoint over Streamable HTTP with --stateless, and authenticate with a long-lived access token from your Home Assistant user profile. No port forwarding, reverse proxy, or Nabu Casa subscription needed.

What’s the minimum Home Assistant version for MCP support?

2025.2 is the minimum, since that’s the release where both mcp_server and mcp were introduced. Run at least that version, though staying current is worth it: both integrations are still under active development, and the current release as of September 2026 is 2026.9.3.


Share this post on:

Send a Webmention

Written about this post on your own site? Send a webmention and it'll show up above once verified.


Previous Post
Proxmox MCP: Read-Only Beats Root
Next Post
Komodo MCP vs the Docker Socket

Discussion

Powered by Garrul . Sign in with GitHub or Google, or post anonymously.

Related Posts