GoodWe MCP Server
Implementación del servidor MCP para inversores GoodWe
Documentación
Servidor MCP de GoodWe Inverter
Servidor MCP para monitorear y controlar inversores solares GoodWe a través de la red local.
Construido sobre la librería goodwe y el SDK de Python del Model Context Protocol.
Basado en la integración de GoodWe para Home Assistant — las definiciones de sensores, modos de operación, ajustes y soporte de familias de inversores están modelados directamente según esa implementación.
Características
- Lee datos de operación en tiempo real: producción fotovoltaica, estado de la batería, importación/exportación de red, consumo de carga
- Lee y escribe todos los ajustes configurables del inversor
- Cambia modos de operación (general, eco, respaldo, reducción de picos, fuera de red, …)
- Controla el límite de exportación a la red y la profundidad de descarga de la batería
- 7 recursos MCP: estado, operación, ajustes, flujo de energía, energía diaria, batería, catálogo de sensores
- 6 plantillas de prompt integradas para flujos de trabajo comunes (resumen de estado, diagnóstico, optimización, …)
- Autenticación con token Bearer para todos los transportes HTTP
- Cuatro modos de transporte: stdio, SSE, HTTP Streamable y servidor (SSE + HTTP Streamable combinados)
- Conexión automática mediante variables de entorno
Requisitos
- Python 3.10+
- Inversor GoodWe accesible en la red local (puerto UDP 8899 o Modbus/TCP puerto 502)
Instalación
# with uv (recommended) — installs the locked dependency set from uv.lock
uv sync
# or a plain (editable) install
uv pip install -e .
Uso
stdio (Claude Desktop)
goodwe-mcp
Añade a ~/.claude/claude_desktop_config.json:
{
"mcpServers": {
"goodwe": {
"command": "goodwe-mcp",
"env": {
"GOODWE_HOST": "192.168.1.100"
}
}
}
}
Transporte SSE
goodwe-mcp --transport sse --port 8080
# Server listens on http://0.0.0.0:8080/sse
Transporte HTTP Streamable
goodwe-mcp --transport streamable-http --port 8080
# Server listens on http://0.0.0.0:8080/mcp
Transporte de servidor (SSE + HTTP Streamable combinados)
Sirve ambos transportes en un único puerto — útil cuando necesitas soportar clientes SSE heredados y clientes HTTP Streamable modernos simultáneamente.
goodwe-mcp --transport server --host 0.0.0.0 --port 8080
# SSE: http://0.0.0.0:8080/sse (GET) and /messages/ (POST)
# Streamable HTTP: http://0.0.0.0:8080/mcp
Opciones
--transport {stdio,sse,streamable-http,server} Transport mode (default: stdio)
--host HOST Bind address for SSE/HTTP (default: 127.0.0.1)
--port PORT Listen port for SSE/HTTP (default: 8000)
--log-level {DEBUG,INFO,WARNING,ERROR} Logging verbosity (default: INFO)
--auth-token TOKEN Bearer token required on all HTTP requests (env: MCP_AUTH_TOKEN)
--base-url URL Public base URL, e.g. https://mcp.example.com (env: MCP_BASE_URL)
--allowed-hosts HOSTS Comma-separated Host header values to accept (env: MCP_ALLOWED_HOSTS)
--allowed-origins ORIGINS Comma-separated Origin header values to accept (env: MCP_ALLOWED_ORIGINS)
Variables de entorno
| Variable | Descripción | Valor predeterminado |
|---|---|---|
GOODWE_HOST | IP / hostname del inversor para conexión automática al inicio | — |
GOODWE_PORT | Puerto UDP/TCP del inversor | 8899 |
GOODWE_FAMILY | Anulación de familia de inversor (ET, EH, BT, BH, ES, EM, BP, DT, MS, NS, XS) | detección automática |
MCP_AUTH_TOKEN | Token Bearer requerido en todas las solicitudes HTTP | — (autenticación deshabilitada) |
MCP_BASE_URL | URL base pública del servidor (usada como URL del emisor OAuth) | http://<host>:<port> |
MCP_ALLOWED_HOSTS | Valores de cabecera Host separados por comas a aceptar; habilita la protección contra rebinding de DNS en enlaces no-loopback | — (protección solo en enlaces loopback) |
MCP_ALLOWED_ORIGINS | Valores de cabecera Origin separados por comas a aceptar (clientes basados en navegador) | — |
MCP_PORT | Puerto de escucha usado por el comando predeterminado del contenedor y la verificación de salud (solo Docker) | 8000 |
Autenticación
La autenticación con token Bearer es compatible con todos los transportes HTTP (sse, streamable-http, server).
Cuando está habilitada, cada solicitud MCP debe incluir una cabecera Authorization: Bearer <token>.
El endpoint /health siempre está desprotegido para que las sondas de Kubernetes sigan funcionando.
Habilitar mediante variable de entorno (recomendado)
export MCP_AUTH_TOKEN="$(openssl rand -hex 32)"
goodwe-mcp --transport server --host 0.0.0.0 --port 8080
Habilitar mediante bandera de CLI
goodwe-mcp --transport streamable-http --port 8080 --auth-token my-secret-token
Configuración de Claude Desktop / cliente MCP
Añade el token a la configuración del servidor MCP de tu cliente. Por ejemplo, con Claude Desktop
usando el transporte streamable-http a través de un proxy que inyecta la cabecera, o con cualquier
cliente que soporte cabeceras Authorization:
{
"mcpServers": {
"goodwe": {
"url": "http://localhost:8080/mcp",
"headers": {
"Authorization": "Bearer my-secret-token"
}
}
}
}
Docker / Docker Compose
Pasa el token a través del entorno:
MCP_AUTH_TOKEN=my-secret-token GOODWE_HOST=192.168.1.100 \
docker compose -f docs/docker-compose.yml up -d
Kubernetes
Establece el token en docs/k8s/secret.yaml antes de aplicar los manifiestos:
stringData:
GOODWE_HOST: "192.168.1.100"
MCP_AUTH_TOKEN: "my-secret-token"
Si MCP_AUTH_TOKEN está vacío o no está configurado, la autenticación está deshabilitada y todos los endpoints HTTP son accesibles públicamente. El servidor registrará una advertencia al inicio cuando esté vinculado a una dirección no-loopback sin token.
Protección contra rebinding de DNS
El SDK de MCP valida las cabeceras Host y Origin automáticamente cuando el servidor está vinculado a una dirección loopback. Para cualquier otra dirección de enlace (incluido el valor predeterminado de Docker 0.0.0.0), lista los hostnames o pares host:port que los clientes usan legítimamente para alcanzar el servidor:
MCP_ALLOWED_HOSTS="mcp.example.com,192.168.1.10:8000" \
goodwe-mcp --transport server --host 0.0.0.0 --port 8000
Las solicitudes cuya cabecera Host no esté listada se rechazan con HTTP 421. Un sufijo :* coincide con cualquier puerto (por ejemplo, 192.168.1.10:*). Usa MCP_ALLOWED_ORIGINS para permitir adicionalmente clientes basados en navegador desde orígenes específicos. El servidor registra una advertencia al inicio cuando está vinculado a una dirección no-loopback sin esta configuración.
TLS / HTTPS
El servidor MCP en sí no termina TLS. Para cualquier despliegue fuera de localhost, coloca un proxy inverso con terminación TLS delante (nginx, Caddy, Traefik). Servir datos del inversor — que constituyen datos personales según el RGPD cuando se vinculan a un hogar — a través de HTTP plano es un riesgo de seguridad.
Ejemplo con Caddy (la opción más simple):
mcp.example.com {
reverse_proxy localhost:8000
}
Aviso de procesamiento de datos
Este servidor procesa datos de un inversor solar GoodWe, incluida la dirección IP del inversor, el número de serie y las métricas de consumo de energía. Cuando se despliega en un hogar y lo opera el propietario para uso personal, este procesamiento cae bajo la exención doméstica del RGPD (Art. 2(2)(c)) y el RGPD no aplica. Si se despliega comercialmente — por ejemplo, para monitorear inversores pertenecientes a clientes de terceros — el operador se convierte en responsable del tratamiento según el RGPD (UE) 2016/679 y debe establecer una base legal para el procesamiento (Art. 6), mantener registros de las actividades de procesamiento (Art. 30) y garantizar medidas técnicas y organizativas apropiadas (Art. 32), incluido el cifrado TLS y el control de acceso.
Docker
Construcción
Construye para la arquitectura de la máquina actual:
docker build -t goodwe-inverter-mcp:latest .
Construcciones multi-plataforma (amd64 + arm64)
Usa docker buildx para producir una imagen que se ejecute tanto en servidores x86-64 como en placas ARM (Raspberry Pi, Apple Silicon, AWS Graviton, etc.).
Configuración única — crea un constructor que soporte compilación cruzada:
docker buildx create --name multi --driver docker-container --bootstrap --use
Construye ambas plataformas y carga en el daemon local — requiere el almacén de imágenes containerd (habilitado por defecto en Docker Desktop 4.34+; en Linux ejecuta dockerd --snapshotter=overlayfs o habilítalo en /etc/docker/daemon.json):
docker buildx build --platform linux/amd64,linux/arm64 -t goodwe-inverter-mcp:latest --load .
Construye ambas plataformas y envía a un registro (por ejemplo, Docker Hub o GHCR):
docker buildx build \
--platform linux/amd64,linux/arm64 \
-t youruser/goodwe-inverter-mcp:latest \
--push .
Construye ambas plataformas y exporta como un tar OCI local (sin necesidad de registro):
docker buildx build \
--platform linux/amd64,linux/arm64 \
-t goodwe-inverter-mcp:latest \
--output type=oci,dest=goodwe-inverter-mcp.tar .
Ejecución
docker run -d \
--name goodwe-mcp \
-e GOODWE_HOST=192.168.1.100 \
-p 8000:8000 \
goodwe-inverter-mcp:latest
El contenedor usa por defecto --transport server (SSE + HTTP Streamable en el puerto 8000).
Cambia el puerto con MCP_PORT para que la verificación de salud integrada lo siga:
docker run -d -e GOODWE_HOST=192.168.1.100 -e MCP_PORT=9000 -p 9000:9000 \
goodwe-inverter-mcp:latest
Para cambiar el transporte, anula el comando. Mantén MCP_PORT sincronizado, ya que la verificación de salud lo sondea:
docker run -d -e GOODWE_HOST=192.168.1.100 -e MCP_PORT=9000 -p 9000:9000 \
goodwe-inverter-mcp:latest \
goodwe-mcp --transport streamable-http --host 0.0.0.0 --port 9000
Docker Compose
GOODWE_HOST=192.168.1.100 docker compose -f docs/docker-compose.yml up -d
docs/docker-compose.yml usa network_mode: host por defecto para que el contenedor pueda alcanzar el inversor en la LAN local. Elimina esa línea si tu red ya enruta el tráfico LAN hacia los contenedores.
Kubernetes
Requisitos previos
El inversor GoodWe se comunica por UDP/TCP en la red local. El pod necesita alcanzar la IP del inversor. La configuración más simple es hostNetwork: true en un nodo de la misma subred; elimínala si tu clúster tiene redes planas u otra solución de enrutamiento.
Despliegue
# 1. Edit the inverter IP
vi docs/k8s/secret.yaml
# 2. Apply all manifests
kubectl apply -f docs/k8s/
# 3. Check status
kubectl rollout status deployment/goodwe-mcp
kubectl logs -f deployment/goodwe-mcp
Endpoints de salud
Tanto las sondas de liveness como de readiness apuntan a GET /health, que devuelve:
{ "status": "ok", "inverter_connected": true }
El pod se vuelve listo una vez que el servidor HTTP está activo. inverter_connected será false hasta que el servidor se conecte exitosamente al inversor (la conexión automática se dispara en la primera sesión de cliente MCP).
Herramientas
| Herramienta | Descripción |
|---|---|
connect_inverter | Conecta a un inversor GoodWe por IP/hostname |
get_connection_status | Verifica si está conectado y muestra información del dispositivo |
get_device_info | Nombre del modelo, número de serie, versión de firmware |
get_runtime_data | Todos los valores de sensores en vivo (filtro opcional por tipo: PV/AC/BAT/GRID/UPS/BMS) |
list_sensors | Lista todos los IDs y nombres de sensores |
read_sensor | Lee un solo sensor por ID |
get_settings_data | Todos los ajustes configurables y valores actuales |
read_setting | Lee un solo ajuste por ID |
write_setting | Escribe un valor en un ajuste configurable |
get_operation_mode | Modo actual y modos soportados |
set_operation_mode | Establece modo: general, eco, respaldo, fuera_de_red, reducción_de_picos, eco_carga, eco_descarga |
get_grid_export_limit | Límite de exportación a la red en vatios |
set_grid_export_limit | Establece límite de exportación a la red (0 = deshabilitado) |
get_battery_dod | Ajuste de profundidad de descarga de la batería |
set_battery_dod | Establece profundidad de descarga de la batería (0–99%) |
Prompts
Plantillas de prompt preescritas que los clientes MCP pueden obtener y usar directamente.
| Prompt | Argumentos | Descripción |
|---|---|---|
status_overview | — | Informe de estado completo: conexión, flujo de energía en vivo, batería, red |
battery_optimisation | — | Revisa los ajustes de la batería y sugiere mejoras de DoD / modo |
grid_export_config | — | Verifica y ajusta el límite de potencia de exportación a la red |
operation_mode_change | — | Explica los modos disponibles y ayuda a cambiar al correcto |
diagnose_issue | symptom (opcional) | Recopila diagnóstico completo e identifica problemas |
daily_energy_summary | — | Contadores de energía de hoy como tabla legible |
Recursos
| URI | Descripción |
|---|---|
inverter://status | Estado de conexión e información del dispositivo (JSON) |
inverter://runtime | Todos los valores de sensores en vivo (JSON) |
inverter://settings | Todos los ajustes configurables (JSON) |
inverter://power/now | Flujo de energía en tiempo real — PV, batería, red y carga en vatios, agrupados por tipo |
inverter://energy/today | Contadores de energía de hoy — producción, carga, compra/venta de red, carga/descarga de batería (kWh) |
inverter://battery | Sensores de batería, límite de DoD y modo de operación actual en un solo payload |
inverter://sensors | Catálogo estático de sensores — id, nombre, unidad y tipo para cada sensor (sin valores en vivo) |
Familias de inversores
| Familia | Modelos | Notas |
|---|---|---|
| ET / EH / BT / BH | Híbrido trifásico | Soporte de batería, hasta 4 cadenas PV |
| ES / EM / BP | Híbrido monofásico | Soporte de batería |
| DT / MS / NS / XS | Solo conexión a red | Sin batería |
Desarrollo
# install runtime + dev dependencies from the lockfile
uv sync
# run the test suite
uv run pytest
# with a coverage report
uv run pytest --cov=goodwe_mcp --cov-report=term-missing
# lint
uv run ruff check src tests
Las pruebas se ejecutan completamente contra un inversor simulado en memoria, por lo que no se necesita acceso a hardware ni red. Conducen el servidor a través de una sesión real de cliente MCP y las aplicaciones ASGI reales, cubriendo la validación de entrada de herramientas, payloads de recursos, autenticación Bearer, protección contra rebinding de DNS y la CLI. GitHub Actions ejecuta la suite en Python 3.10 hasta 3.13 y construye la imagen del contenedor en cada push y pull request.
Licencia
Consulta el archivo LICENSE en la raíz del repositorio.
Aviso legal
Este software no está afiliado ni respaldado por GoodWe Inc. Úsalo bajo tu propio riesgo. Este software es un proyecto personal que mantengo en mi tiempo libre. Consulta la licencia para más información.