Skip to main content
Glama

solar-plan-mcp

An agent that answers one everyday question: will I get through tomorrow's set of consumers on my own generation, and if not — what should I move?

A roof with panels, a battery, an inverter, a known outage schedule. The agent takes the weather forecast from a ready-made MCP server, calculates expected generation by hour on its own MCP server, checks the plan against physical rules, and if it doesn't pass — moves flexible loads and proves with numbers that things got better.

Two MCP connections:

Server

Role

Ready-made

mschneider82/mcp-openweather, commit e032683

forecast: sky class and temperature for every 3 hours

Own

solar_mcp (this repository)

4 meaningful domain tools + forecast text parsing

Documentation: tool contracts · design rationale · demo scenario

What you need

What for

Note

Python 3.13

agent and own server

no admin rights needed

Go 1.24+

only to build the weather server

the project publishes no prebuilt binaries; go.mod requires 1.24, although the README there says 1.20

OpenWeather key

weather server

free, openweathermap.org/api; takes up to a few hours to activate

claude CLI + model access

only the agent; the own server and tests don't need it

Claude Agent SDK spawns this CLI as a child process — see Model access

Node + npx

optional — MCP Inspector

npx @modelcontextprotocol/inspector

The PVGIS dataset is already in the repository (data/pvgis_kyiv_5kwp.csv, 1.1 MB), so the own server works without a network. Nothing needs to be downloaded.

Related MCP server: Solar MCP

Installation

Windows, and this isn't cosmetic: all commands are in PowerShell, because && is not an operator in PowerShell 5.1 at all. From here on, everywhere .venv\Scripts\python.exe.

First, two encoding variables, and they are different. PYTHONUTF8=1 tells Python to write UTF-8; [Console]::OutputEncoding tells PowerShell to read it the same way. Without the second one, Ukrainian output turns into ╨▓╨╗╨░╤ü╨╜╨╕╨╣ — measured, and precisely in the pipe (| Tee-Object, | Select-String), because there PowerShell decodes bytes with the console code page. Set them in every new window, and before installation, not after:

[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

You need Python 3.13 — the exact one that python --version answers with, because venv inherits the version of the interpreter that created it.

Why versions are pinned. The demo must be reproducible, not "work somewhere". mcp==2.0.0 is already MCPServer instead of FastMCP, and code written for 1.x won't work here; claude-agent-sdk==0.2.144 — on it the tools and the init-message format from which the agent prints both connections are verified; tzdata — because Windows has no system timezone database, and without Europe/Kyiv there won't be a single local hour. The requirements.txt itself is kept in ASCII deliberately: pip reads it with the locale encoding, and a single Cyrillic letter in a comment breaks pip install -r with a UnicodeDecodeError on any machine where the locale isn't UTF-8. Measured on cp1252 from a clean clone — that's why the explanation lives here, not in that file.

Build the weather server

Go installs into the user profile, without an administrator and without registry changes:

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

The agent looks for the ready-made binary at vendor\mcp-openweather\mcp-weather.exe. If yours is located elsewhere — don't move it, but point a variable at it: $env:WEATHER_MCP_BINARY = "…\mcp-weather.exe" (see env.example). The agent checks that the file exists before starting the session and refuses with a sentence, not a traceback from inside the SDK.

vendor/ is in .gitignore: someone else's git history and a 13 MB binary have nothing to do in this repository. The commit is pinned — the contract documentation is written against exactly that one.

If the course pins a different mcp-openweather commit — take it and record it here; the contract description in docs/TOOLS.md was written from main.go at e032683.

Key

Secrets never get into the repository: .env and .env.* are in .gitignore, and the sample lies in env.example without a single value.

The demo needs three terminals, and $env: lives in only one, so the key is worth setting at the user level — no admin rights are needed for that:

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

Only new terminals will see the new value. A check that it arrived without revealing the key: .venv/Scripts/python.exe -c "import os; print(len(os.environ.get('OWM_API_KEY','')))" — should be 32. Don't run dir env: on camera: it prints the key.

The one-off session form $env:OWM_API_KEY = "…" also works, but has exactly one proper use here — clearing the key in a separate window for the failure scenario: $env:OWM_API_KEY = "".

The key is read only from the environment — it's not in the code or in .mcp.json.example; there stands the ${OWM_API_KEY} substitution. The .env file is read by no one: the code only has os.environ.get, so copying env.example into .env is an empty action.

Model access

The own server and all 57 tests work without any Anthropic credentials — these are different things and shouldn't be confused. The model is needed by exactly one file, agent/run.py.

The Claude Agent SDK doesn't talk to the API itself: it spawns the claude CLI as a child process, and it's that CLI that looks for authorization. So two things are needed:

  1. claude in PATH. Check: (Get-Command claude).Source. Installation — per the official instructions; in this project it was installed via WinGet and lives at %LOCALAPPDATA%\Microsoft\WinGet\Links\claude.exe.

  2. Authorization — one of two paths, and the CLI takes whichever it finds:

    • claude login — interactive sign-in; the CLI puts the token in ~/.claude/.credentials.json. This is exactly the path used here: the process environment has no ANTHROPIC_* variable at all, and the credentials file exists. The recorded run of August 25, 2026 went this way.

    • ANTHROPIC_API_KEY in the environment — a key from console.anthropic.com. Set it the same way as OWM_API_KEY above, and likewise it never gets into the repository.

What's pinned in the code: the model claude-opus-5 (agent/run.py) and claude-agent-sdk==0.2.144 (requirements.txt). If you have different access and this model id doesn't resolve — replace it in run.py with an available one and record here which one; the rest of the run doesn't depend on the id.

This code reads no credential and passes none anywhere: agent/run.py touches neither ANTHROPIC_API_KEY nor the credentials file — that's the CLI's job. There are no secrets in the repository, and env.example lies empty.

External API limits

The free OpenWeather plan gives 60 calls per minute (documentation). One agent run makes one call to the weather tool; inside, the weather server turns it into two HTTP requests (current weather + 5-day forecast). That is, three orders of magnitude of headroom below the ceiling even with continuous rehearsals.

The code has no polling loop, no retry on error, and no background refresh: the weather is requested exactly when the model calls the tool. The own server doesn't go to the network at all — its dataset lies in data/, so any number of runs of estimate_pv_generation, validate_energy_plan, and the rest create no external request whatsoever.

Running: two independent processes

The own server starts separately from the agent and knows nothing about the agent.

Terminal 1 — the own MCP server:

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

Terminal 2 — the agent:

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

Without --date the agent plans tomorrow's day: the product question is precisely about tomorrow, and the OpenWeather forecast only covers now … +5 days, so today's day is already half outside the horizon. A date outside that window will give NO_FORECAST_FOR_DATE, not silent zeros.

The agent starts the weather server itself, over stdio — that's how the connection is configured. The own server can also be run over stdio (python -m solar_mcp, that's the default) — that's how clients like Claude Code expect it, and that's the variant described in .mcp.json.example. For the demo, HTTP is better: then it's visible that the server really is a separate process.

Useful agent flags:

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

--date accepts only a day within the forecast horizontomorrow … today + 5. A date outside it will give NO_FORECAST_FOR_DATE, and the presence of an outage window in the schedule doesn't save it: the schedule lies in the repository and knows any date, while the forecast lives five days. Check before running: scripts/call_weather.py --city Kyiv --covers YYYY-MM-DD.

The dates in the outage schedule have a weekly pattern and provenance — see data/outage_windows.json: the windows for August 22–26 are taken from the public schedule, then the same pattern is repeated forward so the demo doesn't depend on the recording date.

Checking that everything is alive

# 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

The tests need neither a network, nor an OpenWeather key, nor model access: the dataset lies in the repository, and the foreign server's responses are recorded in tests/fixtures/.

What lives where

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

Two of these directories have their own README, and they are exactly what's looked for under "data source" and "fixtures": data/README.md — where the PVGIS row, the tariff, and the outage schedule came from; tests/fixtures/README.md — what exactly was recorded from the foreign server, when, and with what.

One observation on which half the design stands

The weather server doesn't distinguish a failure from an empty response. Without a key it returns is_error: false and text with zeros and an empty city name — recorded verbatim in tests/fixtures/owm_no_api_key.txt, although its README promises "FATAL: OWM_API_KEY environment variable not set".

That's why the own server is built the opposite way: a closed list of error codes, a field pointing at the guilty field, and separately — a reason where emptiness is legitimate (night, no violations). Details: DESIGN.md, TOOLS.md.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.

No tool schema history has been recorded yet.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

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
    D
    maintenance
    An MCP server that enables AI assistants to look up solar permitting authorities, estimate solar production via PVWatts, and retrieve irradiance data. It streamlines the creation of solar-aware workflows by integrating industry-standard APIs like NREL.
    MIT
  • F
    license
    Not graded
    quality
    B
    maintenance
    MCP server for querying and simulating the dispatch plan of a solar PV + battery system in the Chilean electricity market, using deterministic optimization and optional DRL.
    -
  • F
    license
    A
    quality
    C
    maintenance
    Enables solar energy feasibility analysis and ROI calculation for Indian users, including irradiance lookup, system sizing, cost estimation, subsidy calculation, and environmental impact assessment.
    8
    -

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/prasolantoncp-bot/solar-plan-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server