Skip to content
Infrastructure

Letting Cursor control my smart home through the Home Assistant MCP server

By Victor Da Luz
home-assistant mcp cursor ai homelab automation
Letting Cursor control my smart home through the Home Assistant MCP server

I already talk to my homelab through AI agents all day. They create Plane issues, run Ansible, poke at containers over SSH. The one thing they couldn’t touch was my smart home. If I wanted to know whether the office lights were on or flip a switch, I left the editor and opened the Home Assistant app.

Home Assistant fixed the hard part for me. Since version 2025.2 it ships a built-in Model Context Protocol server in Core, no add-on or custom package required. I run 2025.6.1, so it was already sitting there waiting to be turned on. This is the story of wiring it into Cursor and the two snags I hit doing it.

The integration that used to be a third-party project

If you go looking for a Home Assistant MCP server, you’ll find a GitHub repo by allenporter that’s now archived. That threw me for a second. The reason it’s archived is the good kind: the functionality got merged into Home Assistant Core. The official integration is just called “Model Context Protocol Server,” and you add it like any other integration from the UI.

That’s the first gotcha. The MCP endpoint does not exist until you enable the integration. I went hunting for /api/mcp before turning it on and got nothing back, which sent me down a pointless debugging path. Enable the integration first, then the endpoint answers.

What the MCP server actually exposes

The integration doesn’t dump your entire entity registry onto the AI. It exposes the entities you’ve exposed to Assist, Home Assistant’s voice assistant layer, through the same LLM API. That detail matters more than it sounds.

When I first connected, the assistant could see a sensible set of devices: lights, switches, media players, motion sensors, covers. More than 50 of them. That worked because I’d already set up my Assist exposure for voice control. If your assistant comes back seeing nothing, the integration is probably connected fine and the real fix is exposing entities to Assist, not anything in the MCP config.

The VLAN problem

My network is segmented. Home Assistant lives on my server VLAN; my Mac is on the client VLAN. The MCP server listens on Home Assistant’s own host and port, and I didn’t want to open direct cross-VLAN access to that box just for this.

I already route to internal services through Traefik, so I pointed the MCP client at the Traefik hostname instead of the raw host:

https://homeassistant.example.net/api/mcp

That gives me a clean TLS endpoint that’s already allowed across the VLAN boundary, and it means the MCP path is the same whether I’m on the client VLAN or anywhere else inside the network. No new firewall holes punched specifically for this.

The transport mismatch, and mcp-proxy

Here’s the second snag. The Home Assistant MCP server speaks Streamable HTTP. Cursor’s MCP client, like a lot of MCP clients, only speaks stdio. They can’t talk to each other directly.

The bridge is mcp-proxy, a small gateway that presents a stdio interface to the client and forwards to an HTTP endpoint upstream. Cursor launches mcp-proxy over stdio; mcp-proxy holds the HTTP connection to Home Assistant.

I install MCP tooling with uv tool install rather than uvx. The difference bit me on another MCP server earlier: uvx caches its environment in the ephemeral uv cache, and any uv cache clean or version bump evicts it, forcing a re-download mid-startup that can crash the server. uv tool install creates a permanent venv with a stable binary, so the path I point Cursor at doesn’t move:

uv tool install mcp-proxy

Then the Cursor config in ~/.cursor/mcp.json points at the installed binary, sets the transport flag, and passes the Home Assistant endpoint plus a long-lived access token:

{
  "mcpServers": {
    "home-assistant": {
      "command": "/Users/me/.local/bin/mcp-proxy",
      "args": ["--transport=streamablehttp", "https://homeassistant.example.net/api/mcp"],
      "env": {
        "API_ACCESS_TOKEN": "<long-lived-access-token>"
      }
    }
  }
}

You generate the long-lived token from your Home Assistant profile page. The MCP server also supports OAuth via IndieAuth, but for a single machine the token is simpler.

Why MCP and not SSH

I already have SSH access to the Home Assistant host, so why add MCP at all? Because they’re for different jobs. SSH is for managing the machine: editing YAML, restarting the service, reading logs. MCP is for talking to the home: “is the garage door open,” “turn off the bedroom lights.” Asking the assistant to control a device over SSH would mean it shelling in and crafting API calls by hand. The MCP server gives it typed tools for exactly that, scoped to the entities I’ve already decided are safe to expose.

Reflection

  • The endpoint doesn’t exist until the integration is enabled. Don’t debug a connection before turning the integration on in the UI.
  • “It sees no devices” is usually an Assist exposure problem, not an MCP problem. The server only surfaces entities exposed to Assist.
  • A reverse proxy you already trust solves the cross-VLAN access cleanly. Reusing the Traefik path beat opening direct access to the Home Assistant host.
  • Know your transports. A Streamable HTTP server plus a stdio client means you need a proxy in between, and mcp-proxy is the standard one.
  • The long-lived token is the whole key to your house. It can see and control every exposed entity, so treat it like a credential, keep it out of shell history, and revoke it from the HA profile page if a machine is lost.

Related reading

Researching BirdNET-Pi for backyard bird detection
Infrastructure

Researching BirdNET-Pi for backyard bird detection

Before buying any hardware, I researched what it would take to run a self-hosted bird-sound ID service on the homelab: which BirdNET-Pi to use, the hardware it needs, and how it fits a segmented network. Here is the plan I landed on, and why I shelved it.

Read
BirdNET-Go, a doorbell cam, and a dynamic mic from the drawer
Infrastructure

BirdNET-Go, a doorbell cam, and a dynamic mic from the drawer

Deploying a self-hosted bird-sound ID service on the homelab for $0: the BirdNET-Pi to BirdNET-Go switch, USB audio passthrough into Docker-in-LXC, pulling doorbell audio through Frigate go2rtc, and the Ansible YAML bug that silently stopped clips from saving.

Read
Researching update automation for the homelab
Infrastructure

Researching update automation for the homelab

Twenty-three self-hosted services and no update process beyond "when I remember". I compared Watchtower, Diun, Renovate, and WUD, looked at unattended-upgrades for system packages, and landed on a hybrid plan.

Read

Ready to Transform Your Career?

Let's work together to unlock your potential and achieve your professional goals.