mcp-tahoma
README.md
# mcp-tahoma
An MCP server for a Somfy TaHoma box, built from the local API's OpenAPI
descriptor (`src/mcp_tahoma/tahoma-openapi.yml`) with [FastMCP](https://gofastmcp.com).
Everything happens on your local network: the server talks to the box directly,
no Somfy cloud involved.
## 1. Enable developer mode and get a token
In the **TaHoma by Somfy** app: *Configure the installation* → *Access the
parameters* of your box → tap **7 times** on the gateway PIN (e.g.
`2001-1234-5678`) to reveal **Developer Mode**, then generate a token.
A token is only readable at creation time, so save it right away.
## 2. Configure
Copy `.env.example` to `.env` and fill in the token:
```sh
cp .env.example .env
```
| Variable | Default | Meaning |
| ------------------------- | ---------------- | ----------------------------------------------------------- |
| `TAHOMA_TOKEN` | *(required)* | Developer mode token |
| `TAHOMA_PIN` | *(discovered)* | Gateway PIN, e.g. `2001-1234-5678`; needed if you own several boxes |
| `TAHOMA_HOST` | *(discovered)* | Hostname or IP of the box, to skip mDNS discovery entirely |
| `TAHOMA_PORT` | `8443` | Local API port |
| `TAHOMA_VERIFY_SSL` | `true` | Verify the gateway certificate (see [TLS](#tls)) |
| `TAHOMA_CA_BUNDLE` | bundled Overkiz CA | Certificate authority to trust |
| `TAHOMA_TIMEOUT` | `30` | HTTP timeout, in seconds |
| `TAHOMA_DISCOVERY_TIMEOUT`| `5` | mDNS discovery timeout, in seconds |
| `TAHOMA_OPENAPI_SPEC` | `tahoma-openapi.yml` | Path to the OpenAPI descriptor |
## 3. Check that it works
```sh
uv run mcp-tahoma --discover # list the boxes visible on this network
uv run mcp-tahoma --check # connect, verify the token, list your devices
```
`--check` prints something like:
```
Gateway : 2001-1234-5678 (mDNS hostname)
Base URL : https://gateway-2001-1234-5678.local:8443/enduser-mobile-web/1/enduserAPI
TLS : certificate verified, hostname verified
API : 2022.1.3-1
Token : accepted, 12 device(s)
- Living room shutter io://2001-1234-5678/12345678
...
```
## 4. Plug it into an MCP client
Claude Code:
```sh
claude mcp add tahoma --env TAHOMA_TOKEN=<your-token> -- uv run --directory /path/to/mcp-tahoma mcp-tahoma
```
Or, in a client that reads a JSON config:
```json
{
"mcpServers": {
"tahoma": {
"command": "uv",
"args": ["run", "--directory", "/path/to/mcp-tahoma", "mcp-tahoma"],
"env": { "TAHOMA_TOKEN": "<your-token>" }
}
}
}
```
The server speaks stdio by default; `--transport http --port 8765` serves HTTP
instead.
## Tools
One tool per local API operation, generated from `tahoma-openapi.yml`:
| Tool | Endpoint |
| ---- | -------- |
| `get_api_version` | `GET /apiVersion` |
| `get_setup` | `GET /setup` |
| `get_gateways` | `GET /setup/gateways` |
| `get_devices` | `GET /setup/devices` |
| `get_device` | `GET /setup/devices/{deviceURL}` |
| `get_device_states` | `GET /setup/devices/{deviceURL}/states` |
| `get_device_state` | `GET /setup/devices/{deviceURL}/states/{name}` |
| `get_devices_by_controllable` | `GET /setup/devices/controllables/{controllableName}` |
| `execute_actions` | `POST /exec/apply` |
| `get_current_executions` | `GET /exec/current` |
| `get_current_execution` | `GET /exec/current/{executionId}` |
| `cancel_all_executions` | `DELETE /exec/current/setup` |
| `cancel_execution` | `DELETE /exec/current/setup/{executionId}` |
| `register_event_listener` | `POST /events/register` |
| `fetch_events` | `POST /events/{listenerId}/fetch` |
| `unregister_event_listener` | `POST /events/{listenerId}/unregister` |
Plus two local helpers: `gateway_connection_info` (which box, which URL, TLS
status) and `discover_local_gateways` (mDNS browse).
Closing a shutter, for instance, is a single `execute_actions` call:
```json
{
"label": "close the living room",
"actions": [
{
"deviceURL": "io://2001-1234-5678/12345678",
"commands": [{ "name": "setClosure", "parameters": [100] }]
}
]
}
```
The commands a device accepts are listed in its `definition.commands`, returned
by `get_devices`.
## How the box is found
1. `TAHOMA_HOST` if set;
2. otherwise `gateway-<TAHOMA_PIN>.local` when `TAHOMA_PIN` is set and resolves;
3. otherwise an mDNS browse for `_kizboxdev._tcp`, matching the `gateway_pin`
TXT record — via python-zeroconf, falling back to `dns-sd` (on macOS the
system mDNS daemon regularly sees the box when python-zeroconf does not).
If the gateway is reached by name, the connection is fully verified; if only an
IP address is available, the certificate chain is still verified but the
hostname cannot be.
## TLS
The box serves a certificate signed by the self-signed Overkiz authority, so
`src/mcp_tahoma/certs/overkiz-root-ca-2048.crt` (from
<https://ca.overkiz.com/overkiz-root-ca-2048.crt>) is bundled and trusted for
this connection only — the system trust store is untouched.
`TAHOMA_VERIFY_SSL=false` disables verification entirely. It is a last resort:
the traffic stays encrypted but nothing proves you are talking to your box.
## Rate limits
There is no rate limiting on the local API, but the gateway is a small device.
Call `get_setup` once at start, then `register_event_listener` and poll
`fetch_events` at most once per second, rather than polling `get_devices`.
Listeners are destroyed after 10 minutes of inactivity or when the box reboots.
## Development
```sh
uv sync
uv run pytest
```
The tests cover the generated tools, discovery parsing and the TLS behaviour
against a throwaway HTTPS server presenting a gateway-like certificate; no real
box is needed.
`src/mcp_tahoma/tahoma-openapi.yml` is a symlink to the descriptor at the
repository root, so the spec has a single source of truth and still ships
inside the wheel.
## References
- [Somfy TaHoma Developer Mode](https://github.com/Somfy-Developer/Somfy-TaHoma-Developer-Mode)
(vendored in `Somfy-TaHoma-Developer-Mode-main/`)
This server cannot be deployed
Maintenance
ActivitySlowing
ResponsivenessNo issues