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).
This server cannot be deployed
Maintenance
ActivityInactive
ResponsivenessNo issues