GoodWe MCP Server

Implementação do servidor MCP para inversores GoodWe

Documentação

GoodWe Inverter MCP Server

Servidor MCP para monitoramento e controle de inversores solares GoodWe pela rede local.

goodwe-inverter-mcp MCP server

Construído sobre a biblioteca goodwe e o SDK Python do Model Context Protocol.

Baseado na integração GoodWe do Home Assistant — as definições de sensores, modos de operação, configurações e suporte a famílias de inversores são modelados diretamente a partir dessa implementação.

Recursos

  • Leitura de dados de operação em tempo real: produção fotovoltaica, estado da bateria, importação/exportação da rede, consumo de carga
  • Leitura e escrita de todas as configurações ajustáveis do inversor
  • Alternância de modos de operação (geral, eco, backup, peak-shaving, off-grid, …)
  • Controle do limite de exportação para a rede e profundidade de descarga da bateria
  • 7 recursos MCP: status, operação, configurações, fluxo de energia, energia diária, bateria, catálogo de sensores
  • 6 modelos de prompt integrados para fluxos de trabalho comuns (visão geral de status, diagnósticos, otimização, …)
  • Autenticação por token Bearer para todos os transportes HTTP
  • Quatro modos de transporte: stdio, SSE, Streamable HTTP e servidor (SSE + Streamable HTTP combinados)
  • Conexão automática via variáveis de ambiente

Requisitos

  • Python 3.10+
  • Inversor GoodWe acessível na rede local (porta UDP 8899 ou Modbus/TCP porta 502)

Instalação

# 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

Adicione ao ~/.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 Streamable HTTP

goodwe-mcp --transport streamable-http --port 8080
# Server listens on http://0.0.0.0:8080/mcp

Transporte servidor (SSE + Streamable HTTP combinados)

Atende ambos os transportes em uma única porta — útil quando você precisa suportar clientes SSE legados e clientes Streamable HTTP modernos simultaneamente.

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

Opções

--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)

Variáveis de ambiente

VariávelDescriçãoPadrão
GOODWE_HOSTIP/hostname do inversor para conexão automática na inicialização
GOODWE_PORTPorta UDP/TCP do inversor8899
GOODWE_FAMILYSobrescrita da família do inversor (ET, EH, BT, BH, ES, EM, BP, DT, MS, NS, XS)detecção automática
MCP_AUTH_TOKENToken Bearer exigido em todas as requisições HTTP— (autenticação desativada)
MCP_BASE_URLURL base pública do servidor (usada como URL do emissor OAuth)http://<host>:<port>
MCP_ALLOWED_HOSTSValores de cabeçalho Host separados por vírgula a aceitar; ativa proteção contra DNS-rebinding em binds não-loopback— (proteção apenas em binds loopback)
MCP_ALLOWED_ORIGINSValores de cabeçalho Origin separados por vírgula a aceitar (clientes baseados em navegador)
MCP_PORTPorta de escuta usada pelo comando padrão do contêiner e pela verificação de saúde (somente Docker)8000

Autenticação

A autenticação por token Bearer é suportada para todos os transportes HTTP (sse, streamable-http, server). Quando ativada, toda requisição MCP deve incluir um cabeçalho Authorization: Bearer <token>. O endpoint /health permanece sempre desprotegido para que as sondas do Kubernetes continuem funcionando.

Ativar via variável de ambiente (recomendado)

export MCP_AUTH_TOKEN="$(openssl rand -hex 32)"
goodwe-mcp --transport server --host 0.0.0.0 --port 8080

Ativar via flag de CLI

goodwe-mcp --transport streamable-http --port 8080 --auth-token my-secret-token

Configuração do Claude Desktop / cliente MCP

Adicione o token à configuração do servidor MCP do seu cliente. Por exemplo, com o Claude Desktop usando o transporte streamable-http por meio de um proxy que injeta o cabeçalho, ou com qualquer cliente que suporte cabeçalhos Authorization:

{
  "mcpServers": {
    "goodwe": {
      "url": "http://localhost:8080/mcp",
      "headers": {
        "Authorization": "Bearer my-secret-token"
      }
    }
  }
}

Docker / Docker Compose

Passe o token pelo ambiente:

MCP_AUTH_TOKEN=my-secret-token GOODWE_HOST=192.168.1.100 \
  docker compose -f docs/docker-compose.yml up -d

Kubernetes

Defina o token em docs/k8s/secret.yaml antes de aplicar os manifests:

stringData:
  GOODWE_HOST: "192.168.1.100"
  MCP_AUTH_TOKEN: "my-secret-token"

Se MCP_AUTH_TOKEN estiver vazio ou não definido, a autenticação é desativada e todos os endpoints HTTP ficam publicamente acessíveis. O servidor registrará um aviso na inicialização quando vinculado a um endereço não-loopback sem token.

Proteção contra DNS rebinding

O SDK MCP valida automaticamente os cabeçalhos Host e Origin quando o servidor está vinculado a um endereço loopback. Para qualquer outro endereço de bind (incluindo o padrão do Docker 0.0.0.0), liste os nomes de host ou pares host:port que os clientes usam legitimamente para acessar o servidor:

MCP_ALLOWED_HOSTS="mcp.example.com,192.168.1.10:8000" \
  goodwe-mcp --transport server --host 0.0.0.0 --port 8000

Requisições cujo cabeçalho Host não esteja listado são rejeitadas com HTTP 421. Um sufixo :* corresponde a qualquer porta (ex.: 192.168.1.10:*). Use MCP_ALLOWED_ORIGINS para permitir adicionalmente clientes baseados em navegador de origens específicas. O servidor registra um aviso na inicialização quando vinculado a um endereço não-loopback sem essa configuração.

TLS / HTTPS

O próprio servidor MCP não encerra TLS. Para qualquer implantação fora de localhost, coloque um proxy reverso com terminação TLS na frente dele (nginx, Caddy, Traefik). Servir dados do inversor — que constituem dados pessoais sob o GDPR quando vinculados a um domicílio — por HTTP simples é um risco de segurança.

Exemplo com Caddy (opção mais simples):

mcp.example.com {
    reverse_proxy localhost:8000
}

Aviso de Processamento de Dados

Este servidor processa dados de um inversor solar GoodWe, incluindo o endereço IP do inversor, número de série e métricas de consumo de energia. Quando implantado em uma residência e operado pelo proprietário para uso pessoal, esse processamento se enquadra na isenção doméstica do GDPR (Art. 2(2)(c)) e o GDPR não se aplica. Se implantado comercialmente — por exemplo, para monitorar inversores pertencentes a clientes terceiros — o operador se torna controlador de dados sob o GDPR (UE) 2016/679 e deve estabelecer uma base legal para o processamento (Art. 6), manter registros das atividades de processamento (Art. 30) e garantir medidas técnicas e organizacionais adequadas (Art. 32), incluindo criptografia TLS e controle de acesso.

Docker

Build

Build para a arquitetura da máquina atual:

docker build -t goodwe-inverter-mcp:latest .

Builds multiplataforma (amd64 + arm64)

Use docker buildx para produzir uma imagem que rode tanto em servidores x86-64 quanto em placas ARM (Raspberry Pi, Apple Silicon, AWS Graviton, etc.).

Configuração única — crie um builder que suporte compilação cruzada:

docker buildx create --name multi --driver docker-container --bootstrap --use

Build de ambas as plataformas e carregamento no daemon local — requer o armazenamento de imagens containerd (ativado por padrão no Docker Desktop 4.34+; no Linux execute dockerd --snapshotter=overlayfs ou ative em /etc/docker/daemon.json):

docker buildx build --platform linux/amd64,linux/arm64 -t goodwe-inverter-mcp:latest --load .

Build de ambas as plataformas e envio para um registry (ex.: Docker Hub ou GHCR):

docker buildx build \
  --platform linux/amd64,linux/arm64 \
  -t youruser/goodwe-inverter-mcp:latest \
  --push .

Build de ambas as plataformas e exportação como tar OCI local (sem necessidade de registry):

docker buildx build \
  --platform linux/amd64,linux/arm64 \
  -t goodwe-inverter-mcp:latest \
  --output type=oci,dest=goodwe-inverter-mcp.tar .

Execução

docker run -d \
  --name goodwe-mcp \
  -e GOODWE_HOST=192.168.1.100 \
  -p 8000:8000 \
  goodwe-inverter-mcp:latest

O contêiner usa por padrão --transport server (SSE + Streamable HTTP na porta 8000).

Altere a porta com MCP_PORT para que a verificação de saúde integrada a acompanhe:

docker run -d -e GOODWE_HOST=192.168.1.100 -e MCP_PORT=9000 -p 9000:9000 \
  goodwe-inverter-mcp:latest

Para alterar o transporte, sobrescreva o comando. Mantenha MCP_PORT em sincronia, pois a verificação de saúde o testa:

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 padrão para que o contêiner alcance o inversor na LAN local. Remova essa linha se sua rede já roteia tráfego LAN para contêineres.

Kubernetes

Pré-requisitos

O inversor GoodWe se comunica por UDP/TCP na rede local. O pod precisa alcançar o IP do inversor. A configuração mais simples é hostNetwork: true em um nó na mesma sub-rede; remova-a se seu cluster tiver rede plana ou outra solução de roteamento.

Implantação

# 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 saúde

Tanto as sondas de liveness quanto as de readiness acessam GET /health, que retorna:

{ "status": "ok", "inverter_connected": true }

O pod fica pronto assim que o servidor HTTP está ativo. inverter_connected será false até que o servidor se conecte com sucesso ao inversor (a conexão automática é disparada na primeira sessão de cliente MCP).

Ferramentas

FerramentaDescrição
connect_inverterConectar a um inversor GoodWe por IP/host
get_connection_statusVerificar se está conectado e mostrar informações do dispositivo
get_device_infoNome do modelo, número de série, versão do firmware
get_runtime_dataTodos os valores de sensores em tempo real (filtro opcional por tipo: PV/AC/BAT/GRID/UPS/BMS)
list_sensorsListar todos os IDs e nomes de sensores
read_sensorLer um único sensor por ID
get_settings_dataTodas as configurações ajustáveis e valores atuais
read_settingLer uma única configuração por ID
write_settingEscrever um valor em uma configuração ajustável
get_operation_modeModo atual e modos suportados
set_operation_modeDefinir modo: geral, eco, backup, off_grid, peak_shaving, eco_charge, eco_discharge
get_grid_export_limitLimite de exportação para a rede em watts
set_grid_export_limitDefinir limite de exportação para a rede (0 = desativado)
get_battery_dodConfiguração de profundidade de descarga da bateria
set_battery_dodDefinir profundidade de descarga da bateria (0–99%)

Prompts

Modelos de prompt pré-escritos que clientes MCP podem buscar e usar diretamente.

PromptArgumentosDescrição
status_overviewRelatório completo de status: conexão, fluxo de energia em tempo real, bateria, rede
battery_optimisationRevisar configurações da bateria e sugerir melhorias de DoD / modo
grid_export_configVerificar e ajustar o limite de exportação de energia para a rede
operation_mode_changeExplicar os modos disponíveis e ajudar a alternar para o correto
diagnose_issuesymptom (opcional)Coletar diagnósticos completos e identificar problemas
daily_energy_summaryContadores de energia de hoje como tabela legível

Recursos

URIDescrição
inverter://statusStatus da conexão e informações do dispositivo (JSON)
inverter://runtimeTodos os valores de sensores em tempo real (JSON)
inverter://settingsTodas as configurações ajustáveis (JSON)
inverter://power/nowFluxo de energia em tempo real — PV, bateria, rede e carga em watts, agrupados por tipo
inverter://energy/todayContadores de energia de hoje — produção, carga, compra/venda da rede, carga/descarga da bateria (kWh)
inverter://batterySensores da bateria, limite de DoD e modo de operação atual em um único payload
inverter://sensorsCatálogo estático de sensores — id, nome, unidade e tipo de cada sensor (sem valores em tempo real)

Famílias de inversores

FamíliaModelosObservações
ET / EH / BT / BHHíbrido trifásicoSuporte a bateria, até 4 strings fotovoltaicos
ES / EM / BPHíbrido monofásicoSuporte a bateria
DT / MS / NS / XSSomente grid-tieSem bateria

Desenvolvimento

# 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

Os testes são executados inteiramente contra um inversor fake em memória, portanto não é necessário acesso a hardware ou rede. Eles conduzem o servidor por uma sessão real de cliente MCP e pelos aplicativos ASGI reais, cobrindo validação de entrada de ferramentas, payloads de recursos, autenticação Bearer, proteção contra DNS-rebinding e a CLI. O GitHub Actions executa a suíte em Python 3.10 a 3.13 e cria a imagem do contêiner a cada push e pull request.

Licença

Consulte o arquivo LICENSE na raiz do repositório.

Aviso legal

Este software não é afiliado nem endossado pela GoodWe Inc. Use por sua conta e risco. Este software é um projeto pessoal que mantenho no meu tempo livre. Consulte a licença para mais informações.