Skip to main content
Glama

solar-plan-mcp

Agente que responde a una pregunta doméstica: ¿podré cubrir mañana este conjunto de consumidores con mi propia generación, y si no — qué traslado?

Tejado con paneles, batería, inversor, calendario de cortes conocido. El agente toma la previsión meteorológica de un servidor MCP ya preparado, calcula la producción esperada por horas en su propio servidor MCP, comprueba el plan contra las reglas físicas y, si no lo supera, traslada las cargas flexibles y demuestra con números que ha mejorado.

Dos conexiones MCP:

Servidor

Rol

Listo

mschneider82/mcp-openweather, commit e032683

previsión: tipo de cielo y temperatura cada 3 horas

Propio

solar_mcp (este repositorio)

4 herramientas sustantivas del dominio + análisis del texto de la previsión

Documentación: contratos de las herramientas · razón de diseño · guion de demostración

Qué se necesita

Para qué

Nota

Python 3.13

agente y servidor propio

no se necesitan permisos de administrador

Go 1.24+

solo para compilar el servidor meteorológico

el proyecto no publica binarios listos; go.mod exige 1.24, aunque el README de allí dice 1.20

Clave de OpenWeather

servidor meteorológico

gratuita, openweathermap.org/api; tarda hasta varias horas en activarse

CLI claude + acceso al modelo

solo el agente; el servidor propio y sus pruebas no lo necesitan

Claude Agent SDK lanza este CLI como proceso hijo — ver Acceso al modelo

Node + npx

opcional — MCP Inspector

npx @modelcontextprotocol/inspector

El conjunto de datos PVGIS ya está en el repositorio (data/pvgis_kyiv_5kwp.csv, 1,1 MB), por lo que el servidor propio funciona sin red. No hay que descargar nada.

Related MCP server: KinSwitch

Instalación

Windows, y no es cosmético: todos los comandos son en PowerShell, porque && en PowerShell 5.1 no es un operador en absoluto. En adelante, siempre .venv\Scripts\python.exe.

Primero dos variables de codificación, y son distintas. PYTHONUTF8=1 le dice a Python que escriba en UTF-8; [Console]::OutputEncoding le dice a PowerShell que también lo lea. Sin la segunda, la salida en ucraniano se convierte en ╨▓╨╗╨░╤ü╨╜╨╕╨╣ — medido, y precisamente en la tubería (| Tee-Object, | Select-String), porque allí PowerShell decodifica los bytes con la página de códigos de la consola. Hay que configurarlas en cada ventana nueva, y antes de la instalación, no después:

[Console]::OutputEncoding = [Text.Encoding]::UTF8
$env:PYTHONUTF8 = "1"
git clone https://github.com/prasolantoncp-bot/solar-plan-mcp.git
cd solar-plan-mcp

python -m venv .venv                       # або: uv venv .venv
.venv\Scripts\python.exe -m pip install -r requirements.txt

Se necesita Python 3.13 — exactamente el que responde a python --version, porque venv hereda la versión del intérprete con el que se creó.

Por qué las versiones están fijadas. La demostración debe ser reproducible, no «funcionar en algún sitio». mcp==2.0.0 ya es MCPServer en lugar de FastMCP, y el código para 1.x no funcionará aquí; claude-agent-sdk==0.2.144 — en esa versión están verificados tools y el formato del mensaje init del que el agente imprime ambas conexiones; tzdata — porque Windows no tiene una base de datos de zonas horarias del sistema, y sin Europe/Kyiv no habrá ninguna hora local. El propio requirements.txt se mantiene en ASCII a propósito: pip lo lee con la codificación de la configuración regional, y una sola letra cirílica en un comentario rompe pip install -r con UnicodeDecodeError en cualquier máquina donde la configuración regional no sea UTF-8. Medido en cp1252 desde un clon limpio — por eso la explicación vive aquí y no en ese archivo.

Compilar el servidor meteorológico

Go se instala en el perfil del usuario, sin administrador y sin cambios en el registro:

# 1. портативний Go у профіль (один раз). curl.exe є у Windows 10 1803+
curl.exe -Lo go.zip https://go.dev/dl/go1.27.0.windows-amd64.zip
Expand-Archive go.zip -DestinationPath "$env:LOCALAPPDATA\Programs"

# 2. клон і збірка. GOROOT і PATH живуть лише в цьому вікні — так і треба
$env:GOROOT = "$env:LOCALAPPDATA\Programs\go"
$env:PATH = "$env:GOROOT\bin;$env:PATH"
New-Item -ItemType Directory -Force vendor | Out-Null
cd vendor
git clone https://github.com/mschneider82/mcp-openweather.git
cd mcp-openweather
git checkout e032683574a0723591445462ef7104d360ad0889
go build -o mcp-weather.exe .
cd ..\..

El agente busca el binario listo en la ruta vendor\mcp-openweather\mcp-weather.exe. Si lo tienes en otro sitio — no lo muevas, sino indica la variable: $env:WEATHER_MCP_BINARY = "…\mcp-weather.exe" (ver env.example). El agente comprueba la existencia del archivo antes de iniciar la sesión y se niega con una frase, no con un traceback desde dentro del SDK.

vendor/ está en .gitignore: la historia git ajena y 13 MB de binario en este repositorio no tienen nada que ver. El commit está fijado — precisamente sobre él está escrita la documentación del contrato.

Si el curso fija otro commit de mcp-openweather — toma ese y anótalo aquí; la descripción del contrato en docs/TOOLS.md se escribió a partir de main.go en e032683.

Clave

Los secretos no entran en el repositorio: .env y .env.* están en .gitignore, y la plantilla está en env.example sin ningún valor.

La demostración necesita tres terminales, y $env: solo vive en uno, por lo que conviene configurar la clave a nivel de usuario — no se necesitan permisos de administrador para ello:

# так ключ не потрапляє ні в скролбек, ні в історію PSReadLine
$s = Read-Host "OWM_API_KEY" -AsSecureString
[Environment]::SetEnvironmentVariable("OWM_API_KEY",
  [Runtime.InteropServices.Marshal]::PtrToStringBSTR(
    [Runtime.InteropServices.Marshal]::SecureStringToBSTR($s)), "User")

El nuevo valor lo verán solo las terminales nuevas. Comprobación de que ha llegado, sin revelar la clave: .venv/Scripts/python.exe -c "import os; print(len(os.environ.get('OWM_API_KEY','')))" — debe dar 32. En cámara no ejecutar dir env:: imprime la clave.

La forma de sesión única $env:OWM_API_KEY = "…" también funciona, pero tiene aquí un único uso correcto — borrar la clave en una ventana aparte para el escenario de fallo: $env:OWM_API_KEY = "".

La clave se lee solo del entorno — ni en el código ni en .mcp.json.example está; allí hay una sustitución ${OWM_API_KEY}. El archivo .env nadie lo lee: en el código solo hay os.environ.get, así que copiar env.example a .env es una acción vacía.

Acceso al modelo

El servidor propio y los 57 tests funcionan sin ninguna credencial de Anthropic — son cosas distintas y no conviene confundirlas. El modelo lo necesita exactamente un archivo, agent/run.py.

Claude Agent SDK no se comunica con la API por sí mismo: lanza el CLI claude como proceso hijo, y es ese CLI el que busca la autorización. Por tanto se necesitan dos cosas:

  1. claude en PATH. Comprobación: (Get-Command claude).Source. Instalación — según la instrucción oficial; en este proyecto está instalado mediante WinGet y se encuentra en %LOCALAPPDATA%\Microsoft\WinGet\Links\claude.exe.

  2. Autorización — una de dos vías, y el CLI toma la que encuentre:

    • claude login — inicio de sesión interactivo; el CLI guarda el token en ~/.claude/.credentials.json. Precisamente esta vía se ha usado aquí: en el entorno del proceso no hay ninguna variable ANTHROPIC_*, pero el archivo de credenciales existe. La ejecución registrada del 25 de agosto de 2026 transcurrió así.

    • ANTHROPIC_API_KEY en el entorno — clave de console.anthropic.com. Se configura igual que OWM_API_KEY arriba, y tampoco entra en el repositorio.

Lo que está fijado en el código: el modelo claude-opus-5 (agent/run.py) y claude-agent-sdk==0.2.144 (requirements.txt). Si tienes otro acceso y este id de modelo no se resuelve — sustitúyelo en run.py por uno disponible y anota aquí cuál; el resto de la ejecución no depende del id.

Este código no lee ninguna credencial ni la transmite a ningún sitio: agent/run.py no accede ni a ANTHROPIC_API_KEY ni al archivo de credenciales — de eso se encarga el CLI. En el repositorio no hay secretos, y env.example está vacío.

Límites de la API externa

El plan gratuito de OpenWeather da 60 llamadas por minuto (documentación). Una ejecución del agente hace una llamada a la herramienta weather; dentro, el servidor meteorológico la convierte en dos peticiones HTTP (tiempo actual + previsión a 5 días). Es decir, hasta el techo hay tres órdenes de margen incluso con ensayos continuos.

En el código no hay ningún bucle de sondeo, reintento ante error ni actualización en segundo plano: el tiempo se consulta exactamente cuando el modelo llama a la herramienta. El servidor propio no sale a la red en absoluto — su conjunto de datos está en data/, por lo que cualquier número de ejecuciones de estimate_pv_generation, validate_energy_plan y el resto no genera ninguna petición externa.

Ejecución: dos procesos independientes

El servidor propio se levanta por separado del agente y no sabe nada de él.

Terminal 1 — servidor MCP propio:

$env:PYTHONUTF8 = "1"
.venv\Scripts\python.exe -m solar_mcp --transport streamable-http --port 8931

Terminal 2 — agente:

[Console]::OutputEncoding = [Text.Encoding]::UTF8
$env:PYTHONUTF8 = "1"
.venv\Scripts\python.exe agent\run.py

Sin --date, el agente planifica la jornada de mañana: la pregunta del producto es precisamente sobre mañana, y la previsión de OpenWeather solo cubre ahora … +5 días, así que la jornada de hoy ya está a medias fuera del horizonte. Una fecha fuera de esa ventana dará NO_FORECAST_FOR_DATE, no ceros silenciosos.

El agente levanta el servidor meteorológico él mismo, por stdio — así está configurada la conexión. El servidor propio también se puede ejecutar por stdio (python -m solar_mcp, es el valor por defecto) — así lo esperan clientes como Claude Code, y es precisamente esa variante la descrita en .mcp.json.example. Para la demostración es mejor HTTP: entonces se ve que el servidor es realmente un proceso aparte.

Indicadores útiles del agente:

--plan boiler:18:2 --plan aircon:18:3    # свій план замість дефолтного (можна кілька разів)
--date YYYY-MM-DD                        # інша доба; вт/чт/пт — без відключень, сб/нд — вечірнє вікно
--objective maximize_outage_reserve      # інша цільова функція
--city Lviv                              # інше місто
--width 120                              # скільки символів сліду друкувати

--date solo acepta una jornada dentro del horizonte de la previsión — mañana … hoy + 5. Una fecha fuera de él dará NO_FORECAST_FOR_DATE, y la presencia de una ventana de corte en el calendario no lo salva: el calendario está en el repositorio y conoce cualquier fecha, pero la previsión vive cinco días. Comprobar antes de ejecutar: scripts/call_weather.py --city Kyiv --covers YYYY-MM-DD.

Las fechas del calendario de cortes tienen un patrón semanal y procedencia — ver data/outage_windows.json: las ventanas del 22 al 26 de agosto están tomadas del calendario público, y a partir de ahí el mismo patrón se repite hacia adelante para que la demostración no dependa de la fecha de grabación.

Comprobación de que todo está vivo

# 4 інструменти домену + 1 допоміжний, зі схемами входу І виходу
.venv\Scripts\python.exe scripts\inspect_tools.py
.venv\Scripts\python.exe scripts\inspect_tools.py --url http://127.0.0.1:8931/mcp --schemas

# сервер погоди напряму: сирий текст і те, що з нього вийшло
.venv\Scripts\python.exe scripts\call_weather.py --city Kyiv

# 57 тестів: фізика, правила домену, планувальник, контракт через MCP-клієнта
$env:PYTHONUTF8 = "1"; $env:PYTHONPATH = "."
.venv\Scripts\python.exe -m pytest tests\ -q

Los tests no necesitan ni red, ni clave de OpenWeather, ni acceso al modelo: el conjunto de datos está en el repositorio, y las respuestas del servidor ajeno están grabadas en tests/fixtures/.

Qué hay dónde

solar_mcp/            власний MCP-сервер (окремий процес)
  server.py           інструменти й ресурс — увесь контракт
  models.py           схеми входу й виходу (Pydantic → справжні inputSchema/outputSchema)
  errors.py           закритий перелік кодів; помилка ≠ порожній результат
  pv.py               огинаюча ясного неба × прозорість × температурний дерейтинг
  rules.py            симуляція балансу, порушення, планувальник, порівняння
  forecast.py         розбір плоского тексту сервера погоди
  dataset.py store.py читання датасету; реєстр виданих оцінок
agent/run.py          Claude Agent SDK, дві MCP-конекції, слід викликів
scripts/              inspect_tools.py — контракт; call_weather.py — чужий сервер напряму
data/                 датасет + fetch_pvgis.py (провенанс)
tests/                57 тестів; у fixtures/ — три записані відповіді сервера погоди й одна синтетична
docs/                 TOOLS.md · DESIGN.md · DEMO.md

Dos de estos directorios tienen su propio README, y son precisamente los que se buscan bajo «fuente de datos» y «fixtures»: data/README.md — de dónde se ha tomado la fila de PVGIS, la tarifa y el calendario de cortes; tests/fixtures/README.md — qué exactamente se ha grabado del servidor ajeno, cuándo y con qué.

Una observación sobre la que se sostiene la mitad del diseño

El servidor meteorológico no distingue una avería de una respuesta vacía. Sin clave devuelve is_error: false y un texto con ceros y el nombre de la ciudad vacío — grabado literalmente en tests/fixtures/owm_no_api_key.txt, aunque su README promete «FATAL: OWM_API_KEY environment variable not set».

Por eso el servidor propio está hecho al revés: lista cerrada de códigos de error, field que señala el campo culpable, y por separado — reason allí donde el vacío es legítimo (noche, ausencia de infracciones). Detalles: DESIGN.md, TOOLS.md.

Related MCP Connectors

Related MCP Servers

  • F
    license
    Not graded
    quality
    D
    maintenance
    Enables AI-powered analysis and prediction of household energy consumption through machine learning models, providing historical consumption breakdowns, price queries from Spanish electricity markets, and personalized energy optimization recommendations.
    -
  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables AI agents to manage household schedules by checking availability, proposing minimal changes, and requiring explicit human approval before executing any action.
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables conversational home power management by providing real-time solar, battery, and grid status, plus forecasts to answer questions like whether you can run an appliance. It supports Victron and Growatt inverters, remembers household appliances and grid events, and renders an interactive dashboard card in MCP-compatible clients.
    5 npm
    MIT
  • A
    license
    Not graded
    quality
    A
    maintenance
    Enables AI assistants to size hybrid renewable energy systems by simulating candidate designs hour-by-hour for a full year, filtering out constraint violations, and ranking remaining options by net present cost and related techno-economic metrics.
    27 PyPI
    Apache 2.0