Skip to main content
Glama
README.md
# ecobee-mcp

A remote [Model Context Protocol](https://modelcontextprotocol.io/) server for one Ecobee account, hosted on Cloudflare Workers. It reads thermostat status and controls modes, holds, schedules, settings, vacations, fan time, and screen messages.

One Worker serves a bearer-protected, stateless `/mcp` endpoint. A Durable Object owns and refreshes the account's access tokens.

## Setup

```sh
npm install
npx wrangler login
cp wrangler.jsonc wrangler.local.jsonc
```

In `wrangler.local.jsonc`, replace `ecobee.example.com` with a hostname on a Cloudflare zone you manage. Deploy using that local config:

```sh
WRANGLER_CONFIG=wrangler.local.jsonc npm run deploy
npx wrangler secret put ECOBEE_USERNAME --config wrangler.local.jsonc
npx wrangler secret put ECOBEE_PASSWORD --config wrangler.local.jsonc
openssl rand -hex 32 | tee /dev/stderr | npx wrangler secret put MCP_BEARER --config wrangler.local.jsonc
```

The custom-domain route creates its DNS record on first deploy.

## Tools

| Tool | Description |
|---|---|
| `get_thermostats` | Summarize all registered thermostats |
| `get_thermostat` | Get selected raw details for one thermostat |
| `set_hvac_mode` | Change HVAC mode |
| `set_hold` | Set heat and cool hold temperatures |
| `resume_schedule` | Cancel holds and resume the programmed schedule |
| `set_fan_min_on_time` | Set minimum fan minutes per hour |
| `update_settings` | Pass through raw thermostat settings |
| `update_program` | Pass through comfort profiles or a weekly schedule |
| `set_comfort_sensors` | Choose which sensors a comfort setting (Home/Away/Sleep…) averages, by sensor name |
| `create_vacation` | Create a vacation event |
| `delete_vacation` | Delete a vacation event |
| `send_message` | Show a message on the thermostat screen |

Write tools change the real thermostat. Confirm changes with the user first.

## Raw HTTP

The endpoint uses stateless Streamable HTTP and returns SSE-framed MCP responses.

```sh
curl -s https://ecobee.example.com/mcp -X POST \
  -H 'Authorization: Bearer <MCP_BEARER>' \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'
```

`GET /` identifies the MCP endpoint. `GET /health` is public and reports whether the Durable Object has tokens without making a network request.

## Local development

```sh
cp .dev.vars.example .dev.vars
npm run check
npx wrangler dev
```

## Notes

- Authentication uses the Ecobee web app's public Auth0 client and password grant. This is unofficial and may break if Ecobee changes that flow.
- No browser authorization is needed.
- Sensor participation is per comfort setting, but Ecobee's Follow Me (`followMeComfort`) overrides it with whichever sensors detect motion. `get_thermostats` shows both; turn Follow Me off with `update_settings` `{"followMeComfort": false}` for strict per-setting sensors.
- Tool temperatures are plain °F. Raw settings and program temperature fields use °F × 10 where noted.
- Credentials are Worker secrets and are sent only to Ecobee's Auth0 token endpoint.
- `/mcp` requires `MCP_BEARER`; `/` and `/health` are public.

## License

MIT. See [LICENSE](LICENSE).