tapo-mcp
MCP server that exposes Tapo smart-home devices as AI-callable tools and resources via the Model Context Protocol.
Built on the tapo crate and the rmcp SDK. Runs as an HTTP server (Streamable HTTP transport).
Example Prompts
"List all my Tapo devices"
"Turn off the office light"
"Turn on smart plug 4 on the power strip"
"Is the office light on?"
"Set the bedroom light to 50% brightness"
"Change the living room light to Coral"
"Take a snapshot from the baby monitor"
"What's the temperature in the kitchen?"
"Show me the last 24 hours of temperature from the living room temperature and humidity sensor"
"List the 5 most recent events on the smart button"
| Tool | Description |
|---|
list_devices | List available Tapo devices on the network (includes set and get capabilities). |
check_device | Verify a device ID matches at a given IP. |
get_device_state | Get a device's current state (e.g. {"type": "DeviceInfo"}). Runs check_device first. |
control_device | Control a device by applying one or more set capabilities. Runs check_device first. |
take_snapshot | Capture a still JPEG snapshot from a Tapo camera (~640x360). Runs check_device first. |
Resources
| URI | Description |
|---|
tapo://devices | JSON list of discovered Tapo devices. |
Capabilities
Devices and child devices expose separate lists of set and get capabilities they support.
Set Capabilities
| Capability | Description |
|---|
Brightness | Set the device brightness (1-100) |
Color | Set the device color using a preset name |
OnOff | Turn the device on or off |
Get Capabilities
| Capability | Description |
|---|
DeviceInfo | Read the device's current state |
Snapshot | Capture a still JPEG snapshot. Served by the dedicated take_snapshot tool (binary, not JSON state) |
TemperatureHumidityRecords | Read the last 24 hours of temperature and humidity records (T310, T315) at 15 minute intervals |
TriggerLogs | Read paginated trigger logs from a hub child sensor (S200, T100, T110, T300) |
Configuration
All configuration is via environment variables prefixed with TAPO_MCP_:
| Variable | Required | Default | Description |
|---|
TAPO_MCP_USERNAME | Yes | β | Tapo account email |
TAPO_MCP_PASSWORD | Yes | β | Tapo account password |
TAPO_MCP_CAMERA_USERNAME | No | β | Camera account username1. Required by take_snapshot. |
TAPO_MCP_CAMERA_PASSWORD | No | β | Camera account password1. Required by take_snapshot. |
TAPO_MCP_DISCOVERY_TARGET | Yes | β | Network target for device discovery (e.g. 192.168.1.255) |
TAPO_MCP_HTTP_ADDR | No | 127.0.0.1:3000 | Address the server listens on |
TAPO_MCP_DISCOVERY_TIMEOUT | No | 5 | Discovery timeout in seconds |
TAPO_MCP_API_KEY | No | β | Bearer token for HTTP authentication (see below) |
TAPO_MCP_ALLOWED_HOSTS | No | loopback only | Comma-separated Host header allowlist (see Network exposure) |
Authentication
When TAPO_MCP_API_KEY is set, the server requires all HTTP requests to include an Authorization: Bearer <key> header. Requests with a missing or invalid token receive a 401 Unauthorized response.
When the variable is unset (or empty/whitespace-only), the server runs without authentication.
Network exposure
The server enforces the MCP Streamable HTTP DNS-rebinding protection. By default only loopback Host headers (localhost, 127.0.0.1, ::1) are accepted, which prevents a malicious web page from reaching a locally running server via DNS rebinding. Requests with any other Host receive a 403 Forbidden response.
To reach the server over the LAN or from another host, set TAPO_MCP_ALLOWED_HOSTS to the exact hostname(s) or host:port authorities clients connect to, for example TAPO_MCP_ALLOWED_HOSTS="tapo-mcp.lan:3000,192.168.1.50:3000". This replaces the loopback default, so include loopback entries as well if you still need them.
To avoid shipping unauthenticated smart-home control, the server refuses to start when it binds to a non-loopback address (for example 0.0.0.0:3000) without TAPO_MCP_API_KEY set. Set an API key, or bind to a loopback address.
Deployment
Docker
docker run --rm \
--network host \
-e TAPO_MCP_USERNAME="you@example.com" \
-e TAPO_MCP_PASSWORD="<YOUR_TAPO_PASSWORD>" \
-e TAPO_MCP_CAMERA_USERNAME="<YOUR_CAMERA_ACCOUNT_USERNAME>" \
-e TAPO_MCP_CAMERA_PASSWORD="<YOUR_CAMERA_ACCOUNT_PASSWORD>" \
-e TAPO_MCP_DISCOVERY_TARGET="192.168.1.255" \
-e TAPO_MCP_API_KEY="<YOUR_TAPO_MCP_API_KEY>" \
ghcr.io/mihai-dinculescu/tapo-mcp:latest
Note: The image binds to 0.0.0.0:3000, so TAPO_MCP_API_KEY is required β the server refuses to start on a non-loopback address without it (see Network exposure). To reach the server by hostname or LAN IP rather than loopback, also set TAPO_MCP_ALLOWED_HOSTS.
Note: --network host is required so the container can reach Tapo devices on your local network via UDP broadcast for discovery. On macOS and Windows, --network host is not supported β you can use -p 3000:3000 instead, but device discovery won't work as Docker Desktop runs containers inside a VM without LAN access.
Kubernetes
Create the Secret and ConfigMap first:
kubectl create secret generic tapo-mcp-secrets \
--from-literal=TAPO_MCP_USERNAME="you@example.com" \
--from-literal=TAPO_MCP_PASSWORD="<YOUR_TAPO_PASSWORD>" \
--from-literal=TAPO_MCP_CAMERA_USERNAME="<YOUR_CAMERA_ACCOUNT_USERNAME>" \
--from-literal=TAPO_MCP_CAMERA_PASSWORD="<YOUR_CAMERA_ACCOUNT_PASSWORD>" \
--from-literal=TAPO_MCP_API_KEY="<YOUR_TAPO_MCP_API_KEY>"
kubectl create configmap tapo-mcp-config \
--from-literal=TAPO_MCP_DISCOVERY_TARGET="192.168.1.255"
Then apply the Deployment:
apiVersion: apps/v1
kind: Deployment
metadata:
name: tapo-mcp
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
app: tapo-mcp
template:
metadata:
labels:
app: tapo-mcp
spec:
hostNetwork: true
containers:
- name: tapo-mcp
image: ghcr.io/mihai-dinculescu/tapo-mcp:latest
env:
- name: TAPO_MCP_USERNAME
valueFrom:
secretKeyRef:
name: tapo-mcp-secrets
key: TAPO_MCP_USERNAME
- name: TAPO_MCP_PASSWORD
valueFrom:
secretKeyRef:
name: tapo-mcp-secrets
key: TAPO_MCP_PASSWORD
- name: TAPO_MCP_CAMERA_USERNAME
valueFrom:
secretKeyRef:
name: tapo-mcp-secrets
key: TAPO_MCP_CAMERA_USERNAME
- name: TAPO_MCP_CAMERA_PASSWORD
valueFrom:
secretKeyRef:
name: tapo-mcp-secrets
key: TAPO_MCP_CAMERA_PASSWORD
- name: TAPO_MCP_API_KEY
valueFrom:
secretKeyRef:
name: tapo-mcp-secrets
key: TAPO_MCP_API_KEY
- name: TAPO_MCP_DISCOVERY_TARGET
valueFrom:
configMapKeyRef:
name: tapo-mcp-config
key: TAPO_MCP_DISCOVERY_TARGET
Note: hostNetwork: true is required for UDP broadcast discovery, similar to --network host in Docker. Because clients reach the server by node IP or hostname rather than loopback, set TAPO_MCP_ALLOWED_HOSTS accordingly (see Network exposure).
OpenClaw
The tapo skill for OpenClaw makes it easy to use a deployed tapo MCP server from OpenClaw agents.
Install it with:
Contributing
Contributions are welcome and encouraged! See /tapo-mcp/CONTRIBUTING.md.