Skip to main content
Glama

solar-plan-mcp

Ein Agent, der eine alltägliche Frage beantwortet: schaffe ich morgen diesen Satz Verbraucher mit meiner eigenen Erzeugung, und wenn nicht – was verschiebe ich?

Dach mit Modulen, Batterie, Wechselrichter, bekannter Abschaltplan. Der Agent holt die Wettervorhersage von einem fertigen MCP-Server, berechnet die erwartete Erzeugung stundenweise auf einem eigenen MCP-Server, prüft den Plan gegen physikalische Regeln, und wenn er nicht durchgeht – verschiebt er flexible Lasten und belegt mit Zahlen, dass es besser geworden ist.

Zwei MCP-Verbindungen:

Server

Rolle

Fertig

mschneider82/mcp-openweather, Commit e032683

Vorhersage: Himmelsklasse und Temperatur alle 3 Stunden

Eigen

solar_mcp (dieses Repository)

4 inhaltliche Domänen-Tools + Textanalyse der Vorhersage

Dokumentation: Tool-Verträge · Design-Begründung · Demonstrationsszenario

Was benötigt wird

Wofür

Hinweis

Python 3.13

Agent und eigener Server

keine Administratorrechte nötig

Go 1.24+

nur um den Wetterserver zu bauen

das Projekt veröffentlicht keine fertigen Binaries; go.mod verlangt 1.24, obwohl das README dort 1.20 schreibt

OpenWeather-Schlüssel

Wetterserver

kostenlos, openweathermap.org/api; aktiviert sich erst nach bis zu einigen Stunden

claude CLI + Modellzugang

nur Agent; eigener Server und Tests brauchen ihn nicht

Das Claude Agent SDK startet diese CLI als Kindprozess – siehe Modellzugang

Node + npx

optional – MCP Inspector

npx @modelcontextprotocol/inspector

Der PVGIS-Datensatz liegt bereits im Repository (data/pvgis_kyiv_5kwp.csv, 1,1 MB), daher arbeitet der eigene Server ohne Netzwerk. Es muss nichts heruntergeladen werden.

Related MCP server: KinSwitch

Installation

Windows, und das ist keine Kosmetik: alle Befehle laufen in PowerShell, denn && ist in PowerShell 5.1 überhaupt kein Operator. Im Folgenden überall .venv\Scripts\python.exe.

Zuerst zwei Codierungsvariablen, und sie sind verschieden. PYTHONUTF8=1 sagt Python, UTF-8 zu schreiben; [Console]::OutputEncoding sagt PowerShell, es ebenso zu lesen. Ohne die zweite wird die ukrainische Ausgabe zu ╨▓╨╗╨░╤ü╨╜╨╕╨╣ – gemessen, und genau in der Pipe (| Tee-Object, | Select-String), denn dort dekodiert PowerShell die Bytes mit der Codepage der Konsole. In jedem neuen Fenster setzen, und vor der Installation, nicht danach:

[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

Benötigt wird Python 3.13 – genau das, das auf python --version antwortet, denn venv erbt die Version des Interpreters, mit dem es erstellt wurde.

Warum die Versionen gepinnt sind. Die Demonstration soll reproduzierbar sein, nicht „irgendwo funktionieren“. mcp==2.0.0 ist bereits MCPServer statt FastMCP, und der Code für 1.x funktioniert hier nicht; claude-agent-sdk==0.2.144 – darauf sind tools und das Format der Init-Nachricht geprüft, aus dem der Agent beide Verbindungen ausgibt; tzdata – denn Windows hat keine systemweite Zeitzonen-Datenbank, und ohne Europe/Kyiv gibt es keine einzige lokale Stunde. Die requirements.txt selbst ist absichtlich in ASCII gehalten: pip liest sie mit der Locale-Codierung, und ein einziges kyrillisches Zeichen in einem Kommentar lässt pip install -r mit UnicodeDecodeError auf jeder Maschine scheitern, deren Locale nicht UTF-8 ist. Gemessen auf cp1252 aus einem frischen Klon – deshalb lebt die Erklärung hier und nicht in jener Datei.

Wetterserver bauen

Go wird im Benutzerprofil installiert, ohne Administrator und ohne Änderungen an der Registry:

# 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 ..\..

Das fertige Binary sucht der Agent unter vendor\mcp-openweather\mcp-weather.exe. Liegt es bei dir anders – nicht verschieben, sondern eine Variable setzen: $env:WEATHER_MCP_BINARY = "…\mcp-weather.exe" (siehe env.example). Der Agent prüft die Existenz der Datei vor dem Start der Sitzung und lehnt mit einem Satz ab, nicht mit einem Traceback aus dem SDK.

vendor/ in .gitignore: fremde Git-Historie und 13 MB Binary haben in diesem Repository nichts zu suchen. Der Commit ist fixiert – auf genau ihm basiert die Vertragsdokumentation.

Wenn der Kurs einen anderen Commit von mcp-openweather fixiert – nimm ihn und notiere ihn hier; die Vertragsbeschreibung in docs/TOOLS.md wurde mit main.go auf e032683 geschrieben.

Schlüssel

Geheimnisse gelangen nicht ins Repository: .env und .env.* stehen in .gitignore, und die Vorlage liegt in env.example ohne jeden Wert.

Die Demonstration braucht drei Terminals, und $env: lebt nur in einem, daher lohnt es sich, den Schlüssel auf Benutzerebene zu setzen – Administratorrechte sind dafür nicht nötig:

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

Den neuen Wert sehen nur neue Terminals. Prüfung, dass er angekommen ist, ohne den Schlüssel preiszugeben: .venv/Scripts/python.exe -c "import os; print(len(os.environ.get('OWM_API_KEY','')))" – muss 32 ergeben. Vor der Kamera kein dir env: ausführen: das druckt den Schlüssel.

Die einmalige Sitzungsform $env:OWM_API_KEY = "…" funktioniert ebenfalls, hat hier aber genau eine richtige Anwendung – den Schlüssel in einem separaten Fenster für das Fehlerszenario zurückzusetzen: $env:OWM_API_KEY = "".

Der Schlüssel wird nur aus der Umgebung gelesen – weder im Code noch in .mcp.json.example gibt es ihn; dort steht die Substitution ${OWM_API_KEY}. Die Datei .env liest niemand: im Code steht nur os.environ.get, daher ist das Kopieren von env.example nach .env eine leere Handlung.

Zugang zum Modell

Der eigene Server und alle 57 Tests funktionieren ohne jegliche Anthropic-Zugangsdaten – das sind verschiedene Dinge und sollte man nicht verwechseln. Das Modell braucht genau eine Datei, agent/run.py.

Das Claude Agent SDK spricht die API nicht selbst an: Es startet die claude CLI als Kindprozess, und genau diese CLI sucht die Autorisierung. Daher werden zwei Dinge benötigt:

  1. claude im PATH. Prüfung: (Get-Command claude).Source. Installation – nach der offiziellen Anleitung; in diesem Projekt wurde es über WinGet installiert und liegt unter %LOCALAPPDATA%\Microsoft\WinGet\Links\claude.exe.

  2. Autorisierung – einer von zwei Wegen, und die CLI nimmt den, den sie findet:

    • claude login – interaktive Anmeldung; das Token legt die CLI in ~/.claude/.credentials.json. Genau dieser Weg wurde hier verwendet: In der Prozessumgebung gibt es keine einzige Variable ANTHROPIC_*, die Zugangsdatendatei existiert aber. Der aufgezeichnete Lauf vom 25. August 2026 lief so.

    • ANTHROPIC_API_KEY in der Umgebung – Schlüssel von console.anthropic.com. Wird genauso gesetzt wie OWM_API_KEY oben und gelangt ebenso wenig ins Repository.

Was im Code gepinnt ist: das Modell claude-opus-5 (agent/run.py) und claude-agent-sdk==0.2.144 (requirements.txt). Wenn du anderen Zugang hast und diese Modell-ID nicht aufgelöst wird – ersetze sie in run.py durch eine verfügbare und notiere hier, welche genau; der Rest des Laufs hängt nicht von der ID ab.

Keine einzige Zugangsdatei liest oder übergibt dieser Code: agent/run.py greift weder auf ANTHROPIC_API_KEY noch auf die Zugangsdatendatei zu – das erledigt die CLI. Im Repository gibt es keine Geheimnisse, und env.example liegt leer.

Limits der externen API

Der kostenlose OpenWeather-Plan erlaubt 60 Aufrufe pro Minute (Dokumentation). Ein Agentenlauf macht einen Aufruf des Tools weather; im Inneren verwandelt der Wetterserver ihn in zwei HTTP-Anfragen (aktuelles Wetter + 5-Tage-Vorhersage). Das heißt, bis zur Obergrenze bleiben drei Größenordnungen Reserve, selbst bei ununterbrochenen Proben.

Im Code gibt es keine Abfrageschleife, keinen Wiederholungsversuch bei Fehlern und kein Hintergrund-Update: Das Wetter wird genau dann abgefragt, wenn das Modell das Tool aufruft. Der eigene Server geht überhaupt nicht ins Netz – sein Datensatz liegt in data/, daher erzeugen beliebig viele Läufe von estimate_pv_generation, validate_energy_plan und dem Rest keine einzige externe Anfrage.

Start: zwei unabhängige Prozesse

Der eigene Server wird getrennt vom Agenten gestartet und weiß nichts über den Agenten.

Terminal 1 – eigener MCP-Server:

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

Terminal 2 – Agent:

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

Ohne --date plant der Agent den morgigen Tag: Die Produktfrage betrifft genau morgen, und die OpenWeather-Vorhersage deckt nur jetzt … +5 Tage ab, daher liegt der heutige Tag schon zur Hälfte außerhalb des Horizonts. Ein Datum außerhalb dieses Fensters ergibt NO_FORECAST_FOR_DATE, nicht stille Nullen.

Den Wetterserver startet der Agent selbst, über stdio – so ist die Verbindung konfiguriert. Den eigenen Server kann man ebenfalls über stdio starten (python -m solar_mcp, das ist der Standard) – so erwarten ihn Clients wie Claude Code, und genau diese Variante ist in .mcp.json.example beschrieben. Für die Demonstration ist HTTP besser: Dann sieht man, dass der Server wirklich ein eigener Prozess ist.

Nützliche Agenten-Flags:

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

--date akzeptiert nur einen Tag innerhalb des Vorhersagehorizonts – morgen … heute + 5. Ein Datum außerhalb ergibt NO_FORECAST_FOR_DATE, und das Vorhandensein eines Abschaltfensters im Plan rettet das nicht: Der Plan liegt im Repository und kennt jedes Datum, die Vorhersage lebt fünf Tage. Vor dem Start prüfen: scripts/call_weather.py --city Kyiv --covers YYYY-MM-DD.

Die Daten im Abschaltplan haben ein wöchentliches Muster und eine Herkunft – siehe data/outage_windows.json: Die Fenster für den 22.–26. August stammen aus dem öffentlichen Zeitplan, danach wird dasselbe Muster nach vorne wiederholt, damit die Demonstration nicht vom Aufnahmedatum abhängt.

Prüfung, dass alles lebt

# 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

Die Tests brauchen weder Netzwerk noch OpenWeather-Schlüssel noch Modellzugang: Der Datensatz liegt im Repository, und die Antworten des fremden Servers sind in tests/fixtures/ aufgezeichnet.

Was wo liegt

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

Zwei dieser Verzeichnisse haben ein eigenes README, und genau sie sucht man unter „Datenquelle“ und „Fixtures“: data/README.md – woher die PVGIS-Zeile, der Tarif und der Abschaltplan stammen; tests/fixtures/README.md – was genau vom fremden Server aufgezeichnet wurde, wann und womit.

Eine Beobachtung, auf der die Hälfte des Designs steht

Der Wetterserver unterscheidet einen Defekt nicht von einer leeren Antwort. Ohne Schlüssel gibt er is_error: false und einen Text mit Nullen und leerem Stadtnamen zurück – wörtlich aufgezeichnet in tests/fixtures/owm_no_api_key.txt, obwohl sein README „FATAL: OWM_API_KEY environment variable not set“ verspricht.

Deshalb ist der eigene Server umgekehrt gebaut: eine geschlossene Liste von Fehlercodes, field mit Verweis auf das schuldige Feld, und separat – reason dort, wo Leere legitim ist (Nacht, keine Verstöße). Details: 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.
    Apache 2.0