Skip to main content
Glama
SarjuThakkar

nest-thermostat-mcp

by SarjuThakkar

nest-thermostat-mcp

Voice control for Nest thermostats from a Pebble Index ring, over Google's Smart Device Management API.

Say "set the upstairs to 70" or "how cold is it downstairs" and it happens.

Pebble ring  ──MCP/HTTPS──▶  this server  ──REST──▶  Google SDM  ──▶  thermostats
 (voice)                      (Pi, :8004)             (cloud)

Runs on the same Raspberry Pi as the rest of my home services — see pi-home-services for the Docker Compose orchestration and Cloudflare Tunnel setup.

Why the cloud API, when the vacuum next door is local

Reluctantly. dreame-vacuum-mcp runs entirely on the LAN over Matter, and I wanted the same here. Nest doesn't allow it:

  • Only the 4th-gen (2024) Nest Learning Thermostat speaks Matter. Older units — 3rd gen, Thermostat E, the 2020 Nest Thermostat — expose no Matter endpoint, and Google does not bridge them onto a third-party Matter fabric.

  • There is no documented local API. The old unofficial local protocols died with the Works with Nest shutdown.

So SDM it is, with the consequences that implies: an internet outage takes this out, and every command is a round trip through Google.

Nest Protect is not supported by SDM at all — smoke/CO alarms were only ever in the legacy Works with Nest API, which is deprecated and closed to new users. This server is thermostats only.

Related MCP server: oura-mcp-server

Tools

Tool

What it does

list_thermostats

Which thermostats exist and what they're called

thermostat_status(name)

Temperature, humidity, setpoint, mode, whether it's actively heating/cooling. Omit the name for all of them

set_temperature(name, temperature)

Set the target. Also takes heat/cool for a thermostat in auto mode

set_mode(name, mode)

heat / cool / auto / off

set_eco(name, on)

Energy-saving mode

set_fan(name, on, minutes)

Run the fan on its own

Names are matched against whatever Google reports — the thermostat's custom name if it has one, otherwise its room. "the upstairs thermostat" finds Upstairs, and a typo like "downstars" still resolves. A name that genuinely doesn't exist is reported back with the real list rather than guessed at, because with two thermostats a wrong guess changes the temperature in the wrong part of the house.

API behaviour worth knowing

Verified against the SDM documentation; the awkward parts are handled in the server rather than left to the caller.

  • Everything on the wire is Celsius, always — even when the thermostat displays Fahrenheit. The display unit comes from the Settings trait, and this server converts in both directions so a spoken "70" means 70 in whatever unit that thermostat shows.

  • Which setpoint command is legal depends on the current mode. SetHeat only works in HEAT, SetCool only in COOL, SetRange only in HEATCOOL. Sending the wrong one is a 400, so the mode is read before every write and the right command chosen.

  • Auto mode is genuinely ambiguous for a single number. "Set it to 70" has no correct meaning for a thermostat holding a range, so the server says so and asks for both ends instead of picking one.

  • Eco mode silently overrides the setpoint. Writing a setpoint with Eco on is accepted and does nothing visible. set_temperature therefore turns Eco off first and says that it did, rather than reporting a temperature that won't be held.

  • A mis-transcribed number is a real hazard when the output is a furnace. Anything outside 45–95°F is refused with an explanation.

  • Offline thermostats don't report a stale temperature. If Google says the device is offline, the status says only that.

Setup

The Google side is manual and has to be done once, by hand, in a browser.

1. Register for Device Access — $5, one-time, non-refundable

https://console.nest.google.com/device-access → accept the terms, pay, then Create project. Note the project ID (a UUID).

2. Create an OAuth client

In Google Cloud Console for the same account:

  1. APIs & Services → Library → enable Smart Device Management API.

  2. APIs & Services → OAuth consent screenExternal. (Internal is only selectable on a Google Workspace account; a personal Gmail has no organisation, so it's greyed out.)

  3. Then click "Publish app" so the status reads "In production". This matters more than it looks: while the status is Testing, Google revokes the refresh token after 7 days, and the server starts answering "Google rejected the saved login" once a week until you re-run the OAuth helper. In production the refresh token lasts indefinitely.

    Publishing an app with a sensitive scope normally invites verification, but you can ignore that — an unverified app still works for its own owner. You just get an "unverified app" interstitial during authorisation; click Advanced → Go to … (unsafe) to continue.

  4. APIs & Services → Credentials → Create credentials → OAuth client IDWeb application. Add https://www.google.com as an Authorized redirect URI — exactly, with no trailing slash.

  5. Copy the client ID and client secret.

  6. Back in the Device Access console, paste the client ID into your project.

Use the same Google account the thermostats are on throughout. A project created on a different account builds fine and then reports zero devices.

3. Authorize and get a refresh token

python3 get_refresh_token.py

It prints a URL, you approve access in the browser, you paste back the code from the address bar, and it prints the four environment values.

4. Run it

python3 test_logic.py     # offline tests, no credentials needed
cp .env.example .env      # fill in the four values above
docker build -t nest-mcp .
docker run -p 8004:8000 --env-file .env nest-mcp

Point the Pebble app at https://<your-host>/mcp with the bearer token.

Environment variables

Variable

Meaning

MCP_BEARER_TOKEN

Static token Pebble sends as Authorization: Bearer <token>

NEST_PROJECT_ID

Device Access project ID (UUID)

NEST_CLIENT_ID

OAuth client ID

NEST_CLIENT_SECRET

OAuth client secret

NEST_REFRESH_TOKEN

From get_refresh_token.py; doesn't expire on its own

PORT

Port inside the container (default 8000)

Maintenance

ActivityMaintained
ResponsivenessSyncing

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

Related MCP Servers

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/SarjuThakkar/nest-thermostat-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server