Skip to main content
Glama
RishaanJ

govee-mcp

by RishaanJ
README.md
# govee-mcp

Control a Govee BLE RGBIC LED strip from Claude (or any MCP client) over Bluetooth on macOS.

Built and tested against the **H617A** segmented-RGB strip. Other Govee BLE models in the H6/H7 family use similar protocols and can likely be made to work with small tweaks to the packet format — PRs welcome.

## Why this exists

macOS sandboxes Bluetooth at the *responsible process* level. A bare `python3` invoked from Claude Desktop or Claude Code doesn't get Bluetooth permission, and TCC silently kills the connection. `govee-mcp` solves this with two pieces:

- A **daemon** (`mcp/govee_daemon.py`) you launch from Terminal — Terminal is already approved for Bluetooth, so the BLE calls succeed.
- An **MCP server** (`mcp/server.py`) Claude talks to, which just relays commands to the daemon over a Unix socket.

The bundled `build_app.sh` script builds a minimal ad-hoc signed `.app` around the Python interpreter with `NSBluetoothAlwaysUsageDescription` set in `Info.plist`. This gives macOS a stable identity to grant Bluetooth to, instead of TCC-aborting on every run.

## Requirements

- **macOS** (tested on Sonoma; should work on Sequoia)
- **Python 3.12** installed from [python.org](https://www.python.org/downloads/macos/) — the build script depends on the `Python.framework` layout. Homebrew Python won't work without modification.
- A Govee H617A LED strip (or another model — see [Compatibility](#compatibility))

## Install

```bash
git clone https://github.com/RishaanJ/govee-mcp.git
cd govee-mcp

# 1. Create the venv and install deps
python3.12 -m venv .venv
.venv/bin/pip install -r requirements.txt

# 2. Build the .app bundle (gives macOS something to grant Bluetooth to)
./build_app.sh
# Creates GoveeBLE.app and a ./py launcher with the correct paths.

# 3. Discover your device's UUID
./py scan.py
# Look for "Govee_H6xxx_XXXX" in the output (also written to scan_result.txt).
# On macOS the "address" is an opaque CoreBluetooth UUID, not a MAC.

# 4. Configure
cp config.example.json config.json
# Edit config.json — paste your device UUID into the "address" field.

# 5. Start the daemon (from Terminal — keep this window open, or background it)
./py mcp/govee_daemon.py &
# You should see: Govee daemon listening on /tmp/govee_daemon.sock
```

### Register the MCP server in Claude Code

Add to your `.mcp.json` (or run `claude mcp add`):

```json
{
  "mcpServers": {
    "govee": {
      "command": "/absolute/path/to/govee-mcp/py",
      "args": ["/absolute/path/to/govee-mcp/mcp/server.py"]
    }
  }
}
```

Use absolute paths — the launcher needs to find the bundled interpreter.

## MCP tools

| Tool | Description |
|---|---|
| `turn_on` | Strip on |
| `turn_off` | Strip off |
| `set_brightness(value)` | 0–100 percent |
| `set_color(r, g, b)` | RGB, 0–255 each |
| `set_named_color(name)` | `red`, `green`, `blue`, `white`, `warm`, `cool`, `orange`, `yellow`, `purple`, `pink`, `cyan`, `sunset`, `off` |
| `set_hex_color("#ff4400")` | hex string, with or without `#` |
| `flash(count)` | flash red N times (default 10) |
| `sunset_fade(duration, r, g, b, ...)` | hold a warm color, then ease-out dim to off over `duration` seconds (use 900 for a 15-min sunset) |

## CLI (no Claude required)

You can drive the strip directly from Terminal too:

```bash
./py govee.py on
./py govee.py off
./py govee.py brightness 60
./py govee.py color red
./py govee.py color 255 0 100
./py govee.py color "#ff4400"
./py sunset.py --duration 900       # 15-minute sunset
./py sunset.py --duration 30        # 30-second test
./py sunset.py --color 255 160 60 --duration 60
```

## Compatibility

**Confirmed:** H617A (segmented RGBIC).

Other Govee BLE strips and their OEM rebrands (ihoment, GBK, Minger) advertise under similar names but use slightly different packet formats. The H617A protocol lives in `govee.py` at `color_frame()` — older models may need the legacy `0x02` packet format instead, which `sunset.py --legacy` hints at. If you get it working with another model, please open a PR with the packet format.

## Troubleshooting

**"Govee daemon is not running."** Start it from Terminal: `./py mcp/govee_daemon.py &`. It must be started from Terminal (not from Claude or another shell) so macOS grants Bluetooth access to the correct responsible process.

**Bluetooth permission denied.** Open System Settings → Privacy & Security → Bluetooth. You should see `GoveeBLE` listed (and `Terminal`, if you've used BLE before). Toggle access on.

**Strip is discovered but won't respond.** Open the Govee app on your phone, pair the strip there once, then try again. Some strips need an initial pairing handshake.

**Colors look wrong / packet format errors.** You may have a different model than H617A. See [Compatibility](#compatibility).

## Architecture diagram

```
┌──────────────┐    Unix socket     ┌──────────────────┐    BLE     ┌──────────────┐
│ Claude (MCP) │ ─────────────────► │ govee_daemon.py  │ ─────────► │ Govee strip  │
│ mcp/server.py│   /tmp/govee_      │ (run from        │            │ (H617A)      │
│              │   daemon.sock      │  Terminal)       │            │              │
└──────────────┘                    └──────────────────┘            └──────────────┘
        ▲
        │ stdio
        ▼
   GoveeBLE.app
   (ad-hoc signed Python with
    NSBluetoothAlwaysUsageDescription)
```

The MCP server is stateless — it's only a thin shim. All BLE work, retries, and timing live in the daemon.

## License

MIT — see [LICENSE](LICENSE).