solar_mcp
solar-plan-mcp
一个回答一个日常问题的代理:明天我能否靠自家发电撑过这批用电设备,如果不行——该把什么挪走?
屋顶有光伏板、电池、逆变器,停电时间表已知。代理从现成的 MCP 服务器取天气预报,在自己的 MCP 服务器上按小时计算预期发电量,对照物理规则检查计划,如果计划不通过——就挪动灵活负载,并用数字证明情况变好了。
两个 MCP 连接:
服务器 | 角色 | |
现成的 |
| 预报:每 3 小时的天空类型和温度 |
自有的 |
| 4 个领域内有意义的工具 + 预报文本解析 |
需要什么
用途 | 备注 | |
Python 3.13 | 代理和自有服务器 | 不需要管理员权限 |
Go 1.24+ | 仅用于编译天气服务器 | 项目不发布现成二进制; |
OpenWeather 密钥 | 天气服务器 | 免费,openweathermap.org/api;激活需要最多几个小时 |
| 仅代理;自有服务器和测试都不需要它 | Claude Agent SDK 会把这个 CLI 作为子进程拉起——见模型访问 |
Node + npx | 可选——MCP Inspector |
|
PVGIS 数据集已经放在仓库里(data/pvgis_kyiv_5kwp.csv,1.1 MB),所以自有服务器无需联网即可工作。什么都不用下载。
安装
git clone <цей-репозиторій>
cd solar-plan-mcp
python -m venv .venv # або: uv venv .venv
.venv\Scripts\python.exe -m pip install -r requirements.txtWindows,而且这不是表面功夫:下面所有命令都在 PowerShell 里,因为 && 在 PowerShell 5.1 里根本不是运算符。之后一律用 .venv\Scripts\python.exe。
两个编码变量,而且它们不一样。 PYTHONUTF8=1 告诉 Python 输出 UTF-8;[Console]::OutputEncoding 告诉 PowerShell 同样地读取它。没有第二个,乌克兰语输出就会变成 ╨▓╨╗╨░╤ü╨╜╨╕╨╣——实测过,而且恰恰是在管道(| Tee-Object、| Select-String)里,因为那里 PowerShell 用控制台代码页解码字节。所以每个新窗口都要:
[Console]::OutputEncoding = [Text.Encoding]::UTF8
$env:PYTHONUTF8 = "1"编译天气服务器
Go 装到用户配置文件里,不需要管理员,也不改注册表:
# 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 ..\..代理按路径 vendor\mcp-openweather\mcp-weather.exe 查找现成二进制。如果它在你那里放得不一样——不要移动,而是设置变量:$env:WEATHER_MCP_BINARY = "…\mcp-weather.exe"(见 env.example)。代理会在会话开始之前检查文件是否存在,用一句话拒绝,而不是从 SDK 内部抛堆栈。
vendor/ 在 .gitignore 里:别人的 git 历史和 13 MB 的二进制跟本仓库无关。提交已固定——契约文档就是针对这个提交写的。
如果课程固定的是
mcp-openweather的另一个提交——就用那个并在这里记下来;docs/TOOLS.md里的契约描述是根据e032683上的main.go写的。
密钥
机密不会进仓库:.env 和 .env.* 在 .gitignore 里,而样例放在 env.example 里,没有任何值。
演示需要三个终端,而 $env: 只存在于一个终端里,所以密钥最好设在用户级别——这不需要管理员权限:
# так ключ не потрапляє ні в скролбек, ні в історію PSReadLine
$s = Read-Host "OWM_API_KEY" -AsSecureString
[Environment]::SetEnvironmentVariable("OWM_API_KEY",
[Runtime.InteropServices.Marshal]::PtrToStringBSTR(
[Runtime.InteropServices.Marshal]::SecureStringToBSTR($s)), "User")新值只有新开的终端才能看到。验证它是否到位而不泄露密钥:.venv/Scripts/python.exe -c "import os; print(len(os.environ.get('OWM_API_KEY','')))"——应该是 32。录制时不要运行 dir env::它会打印密钥。
一次性会话形式 $env:OWM_API_KEY = "…" 也能用,但在这里只有一个正确用途——在单独窗口里为故障场景清空密钥:$env:OWM_API_KEY = ""。
密钥只从环境读取——代码里没有,.mcp.json.example 里也没有;那里放的是替换变量 ${OWM_API_KEY}。.env 文件没人读:代码里只有 os.environ.get,所以把 env.example 复制成 .env 是空操作。
模型访问
自有服务器和全部 57 个测试不需要任何 Anthropic 凭据——这是两回事,不该混淆。模型只被一个文件需要,agent/run.py。
Claude Agent SDK 不自己调用 API:它把 claude CLI 作为子进程拉起,而正是那个 CLI 去找授权。所以需要两样东西:
claude在PATH里。 验证:(Get-Command claude).Source。安装按官方说明;在本项目中它是通过 WinGet 安装的,位于%LOCALAPPDATA%\Microsoft\WinGet\Links\claude.exe。授权——两条路之一,CLI 取它找到的那个:
claude login——交互式登录;CLI 把令牌放到~/.claude/.credentials.json。 这里用的正是这条路:进程环境里没有任何ANTHROPIC_*变量,而凭据文件存在。2026 年 8 月 25 日录制的运行就是这样通过的。环境里的
ANTHROPIC_API_KEY——来自 console.anthropic.com 的密钥。 设置方式跟上面的OWM_API_KEY一样,同样不会进仓库。
代码里写死的内容:模型 claude-opus-5(agent/run.py)和 claude-agent-sdk==0.2.144(requirements.txt)。如果你的访问方式不同,这个模型 id 解析不了——就在 run.py 里换成可用的并在这里记下是哪个;其余运行不依赖 id。
这段代码不读取也不传递任何凭据:agent/run.py 既不碰 ANTHROPIC_API_KEY,也不碰凭据文件——那是 CLI 的事。仓库里没有机密,env.example 是空的。
外部 API 限额
OpenWeather 免费套餐给每分钟 60 次调用(文档)。代理跑一次只做一次 weather 工具调用;天气服务器内部把它变成两个 HTTP 请求(当前天气 + 5 天预报)。也就是说离上限还有三个数量级的余量,哪怕连续排练也一样。
代码里没有任何轮询循环、出错重试或后台刷新:天气只在模型调用工具的那一刻被请求。自有服务器完全不联网——它的数据集在 data/ 里,所以无论跑多少次 estimate_pv_generation、validate_energy_plan 和其余工具,都不会产生任何外部请求。
启动:两个独立进程
自有服务器独立于代理启动,对代理一无所知。
终端 1——自有 MCP 服务器:
$env:PYTHONUTF8 = "1"
.venv\Scripts\python.exe -m solar_mcp --transport streamable-http --port 8931终端 2——代理:
[Console]::OutputEncoding = [Text.Encoding]::UTF8
$env:PYTHONUTF8 = "1"
.venv\Scripts\python.exe agent\run.py不带 --date 时,代理规划明天的一天:产品的问题恰恰是关于明天,而 OpenWeather 预报只覆盖现在 … +5 天,所以今天的一天已经有一半在视野之外。超出这个窗口的日期会得到 NO_FORECAST_FOR_DATE,而不是静默的零。
天气服务器由代理自己通过 stdio 拉起——连接就是这样配置的。自有服务器也可以走 stdio 启动(python -m solar_mcp,这是默认方式)——Claude Code 这类客户端就是这样等它的,.mcp.json.example 里描述的也正是这种方案。演示时用 HTTP 更好:这样能看出服务器确实是独立进程。
代理的有用标志:
--plan boiler:18:2 --plan aircon:18:3 # свій план замість дефолтного (можна кілька разів)
--date YYYY-MM-DD # інша доба; вт/чт/пт — без відключень, сб/нд — вечірнє вікно
--objective maximize_outage_reserve # інша цільова функція
--city Lviv # інше місто
--width 120 # скільки символів сліду друкувати--date 只接受预报视野内的一天——明天 … 今天 + 5。超出范围的日期会得到 NO_FORECAST_FOR_DATE,停电时间表里有这个窗口也救不了:时间表在仓库里,知道任何日期,而预报只活五天。启动前验证:scripts/call_weather.py --city Kyiv --covers YYYY-MM-DD。
停电时间表里的日期有周模式和有来源——见 data/outage_windows.json:8 月 22–26 日的窗口来自公开时间表,之后同样的模式向前重复,这样演示就不依赖录制日期。
验证一切都在运行
# 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测试不需要网络、OpenWeather 密钥或模型访问:数据集在仓库里,而外部服务器的响应记录在 tests/fixtures/ 里。
文件布局
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其中两个目录有自己的 README,也正是「数据来源」和「fixtures」要找的:data/README.md——PVGIS 行、电价和停电时间表来自哪里;tests/fixtures/README.md——从外部服务器具体记录了哪些内容、何时、用什么。
一个支撑了半个设计的观察
天气服务器区分不了故障和空响应。没有密钥时它返回 is_error: false 和全是零、城市名为空的文本——逐字记录在 tests/fixtures/owm_no_api_key.txt 里,尽管它的 README 承诺「FATAL: OWM_API_KEY environment variable not set」。
所以自有服务器反着做:封闭的错误码清单、指明出错字段的 field,以及单独在空值合法的地方(夜间、无违规)给出 reason。细节:DESIGN.md、TOOLS.md。
This server cannot be installed
Maintenance
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
Unofficial integration! ## ✨ Key Features ### 💰 Financial Intelligence - **Smart Charging Cost An…
One-call installer quote review plus energy incentives, estimates, scores, and routing for agents.
Personalized timing intelligence for AI agents — ask 'should I do X on this date?'
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
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