关于 swiss-electricity-mcp
swiss-electricity-mcp 为分析人员和公共机构提供瑞士电力生产、消费预测、储能水库及电价数据查询。它结合能源仪表板、ElCom 电价数据和 CKAN 数据集发现,并为结果附带来源与数据出处信息。
主要功能
- 01查询全国电力生产结构与消费预测
- 02查看储能水库水位和电价指数
- 03检索市镇及州级电价基准
- 04比较多个市镇的电价构成
- 05搜索 BFE 与苏黎世开放数据集
- 06附带来源、出处和获取时间
适用场景
- 01比较某市镇不同年份的电价
- 02将本地电价与瑞士或州级中位数对比
- 03查看全国发电结构与消费预测
- 04查找苏黎世的分时电力消费数据集
MCP 相关
该 MCP 服务提供 12 个只读工具,查询 BFE 仪表板、ElCom 电价数据及 CKAN 数据集。支持 stdio 与 Streamable HTTP,无需认证。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
工具
本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。
详细介绍
swiss-electricity-mcp
瑞士电力数据 MCP 服务——三个官方数据源、十二个工具、无需身份验证。
🌍 阅读其他语言版本: 🇩🇪 Deutsch
本项目属于 Swiss Public Data MCP Portfolio——为瑞士公共行政部门协同构建的一组 MCP 服务。
示例问题
“自 2019 年以来,ewz 面向典型学校建筑(用电类别 C3,≈150'000 kWh/a)的电价如何变化?与瑞士中位数相比如何?”
一次对话即可调用 tariff_get_by_municipality(bfs_nr=261, category="C3")和 tariff_get_median_swiss,并返回逐年对比及完整的数据来源信息,可直接用于管理层演示文稿。
演示
内容概览
三个瑞士官方数据源整合为一个 MCP 服务,每个数据源都有专属工具组:
| 来源 | 提供内容 | 数据来源标记 |
|---|---|---|
| Energiedashboard.ch(联邦能源局) | 全国发电结构、用电量预测、水库蓄水量、消费者价格指数 | live_api |
| ElCom 电价数据立方体(通过 LINDAS SPARQL) | 各市镇、类别和年份的电价,并提供完整细分(能源 + 电网使用 + KEV + 附加费用) | sparql |
| opendata.swiss + 苏黎世市 OGD(CKAN) | 原始时间序列数据集发现(例如每 15 分钟的 NE5/NE7 用电量) | live_api |
无需身份验证。 所有端点均为瑞士公共开放政府数据(OGD)。
工具(12 个)
dashboard_* — Energiedashboard.ch(BFE)
dashboard_get_production_mix— 按年份查询发电结构(TWh + %):核能、水力发电、光伏、风能、火电。dashboard_get_consumption_forecast— 当前用电量预测、未来 5 天展望和未来 5 年区间。dashboard_get_storage_lakes— 水库蓄水量(瑞士全国或各地区:瓦莱州、提契诺州、格劳宾登州、中部/东部)——冬季供电的关键指标。dashboard_get_consumer_price_index— 终端用户电价指数(2020-01-01 = 100)。
tariff_* — ElCom(通过 LINDAS SPARQL)
tariff_list_categories— H1–H8(家庭用户)和 C1–C7(商业用户)。C3 ≈ 150'000 kWh/a 是学校建筑的典型参考类别。tariff_get_by_municipality— 按 BFS-Nr、类别和年份范围查询电价,并细分为能源/电网使用/KEV/附加费用。tariff_get_median_swiss— 全国中位数基准。tariff_get_median_canton— 州级中位数(例如苏黎世州)。tariff_compare_municipalities— 并排比较最多 20 个市镇。
consumption_* — opendata.swiss + 苏黎世市 OGD
consumption_search_bfe_datasets— 搜索 BFE 发布的数据集(CKAN)。consumption_search_zurich— 搜索苏黎世市 OGD 数据集(CKAN),包括每 15 分钟的 NE5/NE7 用电量。
状态
electricity_check_status— 检查所有四个上游服务的运行状态(HTTP 状态码 + 延迟 + 整体健康标志)。
安装
从 PyPI 安装
pip install swiss-electricity-mcp
从源代码安装
git clone https://github.com/malkreide/swiss-electricity-mcp.git
cd swiss-electricity-mcp
pip install -e ".[dev]"
在 Claude Desktop 中使用
添加到 claude_desktop_config.json:
{
"mcpServers": {
"swiss-electricity": {
"command": "swiss-electricity-mcp"
}
}
}
云端部署(Streamable HTTP)
SWISS_ELECTRICITY_TRANSPORT=streamable-http \
SWISS_ELECTRICITY_HOST=0.0.0.0 \
SWISS_ELECTRICITY_PORT=8000 \
swiss-electricity-mcp
可在 Render.com、Railway、Fly.io 上运行。
主机绑定(安全)。 在 HTTP 模式下,主机默认设置为
127.0.0.1(仅限环回地址)。仅在容器内使用SWISS_ELECTRICITY_HOST=0.0.0.0绑定所有网络接口,此时网络边界是容器,而不是主机。在开发者机器上设置0.0.0.0会让本地网络中的其他设备也能访问服务(NeighborJack)。
Docker
项目提供了一个多阶段 Dockerfile。服务以非 root 用户(UID 10001)运行,
并针对容器化场景显式设置 SWISS_ELECTRICITY_HOST=0.0.0.0。
docker build -t swiss-electricity-mcp .
docker run --rm -p 8000:8000 swiss-electricity-mcp
可观测性与配置
| 环境变量 | 默认值 | 用途 |
|---|---|---|
SWISS_ELECTRICITY_TRANSPORT | stdio | stdio 或 streamable-http |
SWISS_ELECTRICITY_HOST | 127.0.0.1 | HTTP 绑定主机(仅在容器中设为 0.0.0.0) |
SWISS_ELECTRICITY_PORT | 8000 | HTTP 端口 |
SWISS_ELECTRICITY_LOG_LEVEL | INFO | 日志级别(DEBUG/INFO/WARNING/ERROR) |
SWISS_ELECTRICITY_CORS_ORIGINS | (empty) | 允许的 CORS 来源,以逗号分隔(浏览器客户端);绝不能设为 * |
OTEL_EXPORTER_OTLP_ENDPOINT | (unset) | 设置后启用 OpenTelemetry 跟踪 |
SWISS_ELECTRICITY_ENV | unknown | 跟踪的 deployment.environment 资源属性 |
-
日志记录采用结构化 JSON 格式,输出到 stderr(stdout 保留用于 stdio JSON-RPC 通道)。上游故障会在服务端完整记录,但面向客户端的响应会隐藏相关信息。
-
跟踪默认不启用。安装额外依赖并将其指向收集器:
pip install "swiss-electricity-mcp[otel]" OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 swiss-electricity-mcp每次工具调用都会生成一个 span(
mcp.tool.<name>),此外,每个上游请求还会自动生成 httpx 子 span。不会记录任何参数值或 PII。
架构
┌────────────────────────── MCP client (Claude etc.) ──────────────────────────┐
│ stdio or Streamable HTTP │
└───────────────────────────────────────┬──────────────────────────────────────┘
│ 12 read-only tools (annotated)
┌────────▼──────────┐
│ MCPServer (mcp) │ egress allow-list + HTTPS gate
│ + structlog/OTel │ per-source TTL cache + retry
└───┬────────┬───┬──┘
dashboard_* │ tariff_* │ │ │ consumption_*
▼ ▼ ▼ ▼
┌───────────────────┐ ┌───────────┐ ┌──────────────┐ ┌─────────────────────┐
│ Energiedashboard │ │ LINDAS │ │ opendata.swiss│ │ data.stadt-zuerich.ch│
│ .admin.ch (BFE) │ │ SPARQL │ │ CKAN │ │ CKAN (OGD) │
└───────────────────┘ └───────────┘ └──────────────┘ └─────────────────────┘
混合模式(实时 API + SPARQL + CKAN 发现),无需身份验证。 这种设计恰当的原因有三点:
- 各数据源的延迟特征不同:Energiedashboard 的响应时间约为 200 ms(非常适合实时查询);LINDAS SPARQL 较慢,偶尔会返回 504(因此设置更长的超时时间并重试 3 次);CKAN 仅提供元数据,本质上是安全的。
- 更新频率不同:仪表板每天会更新多次;ElCom 电价每年更新一次;OGD 数据集可以数月保持稳定。按数据源设置 TTL 缓存(600 s / 3600 s)体现了这些差异。
- 与
swiss-energy-mcp的领域划分:该服务涵盖地理与基础设施数据(发电厂、电网线路)。swiss-electricity-mcp涵盖时间序列与电价。两者可以顺畅地组合使用。
来源标注规范
每个工具响应都是一个 Pydantic 信封,包含:
source— 完整的来源归属字符串(例如 "Daten: Bundesamt für Energie (BFE)…")。provenance— 以下值之一,且仅限一个:live_api/sparql/cached/weekly_dump/stale_cache_fallback。retrieved_at— ISO-8601 UTC 时间戳。
这样可以从结构上杜绝意外的来源误标。
弹性
- 重试:最多重试 3 次,即最多尝试 4 次。等待时间按指数增长(基数为 2 s / 4 s / 8 s),每次加入 0.5–1.5 倍的随机抖动,避免服务中断后客户端同时重试。单次等待不会超过 20 s。收到随 429 或 503 响应发送的
Retry-After时,优先遵循该值(再增加 0–25 %,且总时间不超过 20 s)。 - 时间预算:整个调用的时间预算为 25 s,包含所有尝试和等待。如果等待会导致超出预算,则直接结束调用,因此尝试次数可能少于 4 次。
- 5xx + 429:会重试。4xx(429 除外):立即抛出错误(永久性客户端错误)。
- 内存中的 TTL 缓存:按数据源设置 TTL,以减少上游负载,并缩短多步骤 Agent 工作流中的往返延迟。
MCP 原语 — 为什么只使用工具
此服务有意仅公开工具,不公开资源或提示词。数据具有参数化、由查询驱动的特性(市镇 BFS 编号、类别、年份),因此很适合通过工具调用来提供;没有稳定且可枚举的文档集可作为资源公开,也没有需要随服务提供的精选提示词模板。如果未来的用例需要一份固定的“全国生产结构”文档,那么只读的 dashboard_* 工具显然是迁移为资源的候选对象。
项目阶段
阶段 1 — 只读。 全部 12 个工具均为只读(readOnlyHint=true),不执行写入或破坏性操作。阶段转换标准和长期计划见 docs/roadmap.md。安全态势(出口流量、供应链、致命三元组评估)记录在 docs/security-posture.md 中。
MCP 协议版本
此服务通过同一端点支持两个协议时代。客户端在连接上发送的第一个请求决定适用哪个时代;如果之后再声明使用另一个时代,则会被拒绝。
| 时代 | 修订版 | 哪些客户端使用 |
|---|---|---|
initialize 握手 | 2024-11-05 … 2025-11-25 | 当今客户端使用的版本。服务会以请求指定的修订版作答;如果请求指定了更新的版本,则使用 2025-11-25 这一上限。 |
| 每请求信封 | 2026-07-28 | 携带 2026-07-28 _meta 信封的请求会建立现代连接。 |
两个修订版都固定在 tests/test_protocol_version.py 中,并与已安装的 SDK 断言比对,因此 Dependabot 对 mcp 的升级不会悄然更改其中任何一个。握手上限通过已组装的 ASGI 堆栈执行实际 initialize 请求来测定,而不是从常量名称推断。
请注意,SDK 的 LATEST_PROTOCOL_VERSION 是现代时代的别名,并非握手时代的别名——如果只针对它进行固定,当前客户端实际协商使用的时代就可能在无人注意的情况下发生变化。
更新政策。 当检查未通过时,不要盲目编辑常量:请阅读两个修订版之间的规范变更日志,确认服务仍能正常运行,然后一并更新常量、本节、README.de.md 和 CHANGELOG.md。只有通过明确升级 mcp 的次要版本或主要版本,才会采用规范升级;升级须记录在 CHANGELOG.md 中,并通过工具定义锁文件(tool-definitions.lock.json)验证。
服务器为 2026-07-28 原生设置的内容
SDK 支持某个修订版本,不等于服务器会说这种版本。SDK 留给服务器设置的两个方面,以及不设置时会发生什么:
| 方面 | 此服务器的设置 | SDK 未设置时的行为 |
|---|---|---|
serverInfo — 写入每个结果的 _meta 中,而不只是 initialize 回复 | name、title、version、websiteUrl | 不会填入任何内容。未指定版本的服务器会向每个调用方报告 "version": "",无论属于哪个时代,也无论使用哪种传输方式。 |
可缓存方法上的 ttlMs / cacheScope(SEP-2549) | 在 tools/list、server/discover、prompts/list、resources/list、resources/templates/list 上设为 300000 毫秒,范围为 public | CacheHint() 默认值为 ttl_ms=0、scope="private"——在线路上的含义是“已过期,永不共享”。之后每个客户端都会在每次连接时重新列出。 |
2026-07-28 将 serverInfo 从每个连接握手中只出现一次的脚注,变成了运行中流量的标记,这也让空版本值得设为门槛,而不是一笑置之。
这三个空目录是有意设置提示的。MCPServer 会无条件注册它们的处理程序,server/discover 也会将 prompts 和 resources 列入其功能,因此这些内容在线路上确实存在——只是为空。此服务器无法在运行时注册提示词或资源,因此它们在进程整个生命周期内都会保持为空,这也使它们成为这里最适合缓存的内容。
两者都是根据真实响应测量得出的,而不是从构造函数中读取:tests/test_server_identity.py 会在每个时代分别检查各个身份字段,tests/test_cache_hints.py 则会从实时客户端会话中读取提示。两者都针对一个裸的 MCPServer("kontrolle") 设置了负向对照——没有它,如果 SDK 某天开始自行满足断言,测试结果就会一直被误读为“此服务器”设置了这些内容的证据。
测试
# Unit tests (mocked, fast, CI default) — tests/test_unit.py + tests/test_security.py
PYTHONPATH=src pytest -m "not live" -v
# Live tests (hits real upstreams) — tests/test_live.py
PYTHONPATH=src pytest -m live -v
单元测试涵盖各个契约层:成功路径(响应解析)、重试(5xx、429、4xx)、超时(网络错误 → 干净的 UpstreamUnreachableError)、封套/归因不变量,以及安全性(出口允许列表、SPARQL 转义、工具定义锁定)。CI 会在 Python 3.11–3.13 上运行 ruff + pytest -m "not live"。
审核整个项目组合中的 ruff 固定版本
scripts/pin_audit.py 会检查服务器自身的固定版本守卫是否确实有效。它不是 CI 门槛——它需要磁盘上存在同级代码仓库——但在固定版本约定变更或有新服务器加入时,值得运行:
python scripts/pin_audit.py ../*-mcp
它通过黑盒方式进行测量:在前面插入一个带有自身 rev: 的普通第二个 pre-commit 钩子,运行守卫,读取退出代码,然后还原文件。项目组合中曾有两个守卫将该钩子的版本报告为 ruff 固定版本,导致 CI 因一个无人设置的数字而失败。正向对照(错误配置 ruff 钩子自身的 rev)可以区分“作用范围正确”和“根本不读取文件”——没有它,一个忽略配置的守卫看起来也会像是完全正常。
测试数据的来源
tests/fixtures/ 下的测试夹具记录自实时数据源,并标注了日期。每个文件的来源、获取日期、筛选规则和 SHA-256 值见:
tests/fixtures/PROVENANCE.md。
python scripts/record_fixtures.py # re-record
**请求由生产代码构建。**脚本调用
ElComSparqlClient 和 EnergyDashboardClient,并通过 httpx 传输捕获响应,而不是另行重写 SPARQL 查询。若测试夹具回答的问题与服务端所问的问题略有不同,就会悄无声息地验证错误答案——因为它看起来合理。SPARQL 有 40 行时,“略有不同”是常态,而非例外。
两条筛选规则特意不只是“取前 N 条”:
- **储能湖系列延伸到了未来。**最后一个实测日期之后还有测量值为
null的行——记录当天有 94 条。特意保留这些行:没有它们,就无法测试工具会跳过这些行。 - **每个文件都会明确说明哪些数据算作测量值,而不是靠猜。**最初的版本使用“除
date外任意字段非 null”这一规则,但这是错误的:这些未来行确实带有值——即五年参考曲线——只是没有测量值。
对于经过截断的搜索结果,count 保留其真实值:它表示文件中未包含多少条结果。
已知限制
- LINDAS SPARQL 504 超时:LINDAS 公共端点在负载较高时偶尔会返回 504。3 次重试策略可处理暂时性问题;若服务持续不可用,则会触发
UpstreamUnreachableError。 - 没有历史光伏/风电明细:Energiedashboard 仅提供按年份汇总的发电结构。若要查询不到一年的光伏或风电数据,请使用
consumption_search_bfe_datasets。 - 没有 FHIR 或智能电表数据:不在范围内。未来工作可能会添加
swiss-prosumer-mcp或类似服务。 - 年份覆盖范围:ElCom 电价数据始于 2009 年。Energiedashboard 发电结构数据始于 2014 年。
产品组合协同
此服务可与产品组合中的其他服务自然搭配:
- +
swiss-energy-mcp— 将地理/资产数据(发电厂)与时间序列和电价结合,用于全面分析能源基础设施。 - +
meteoswiss-mcp— 将能耗预测与天气关联起来(温度会影响供暖/制冷负荷)。 - +
fedlex-mcp— 将电价数据与《电力供应法》(StromVG)结合,提供合规/法律背景。 - +
zh-education-mcp— 组合电价、学校数量和基础设施预算,回答与 Schulamt 相关的问题。
数据来源与许可
所有上游数据均为瑞士开放政府数据(OGD-CH):
- Energiedashboard.ch © 联邦能源局 BFE — 开放数据,可免费使用。
- ElCom / LINDAS © 瑞士联邦电力委员会 ElCom — CC BY 4.0。
- opendata.swiss © 瑞士各公共机构 — 大多采用 CC0 / CC BY 4.0。
- 苏黎世市 OGD © 苏黎世市 — CC0。
此 MCP 服务采用 MIT 许可(见 LICENSE)。请始终注明原始数据来源——响应封装会自动包含正确的署名字符串。
贡献
请参阅 CONTRIBUTING.md。
安全
安全政策及漏洞报告方式请参阅 SECURITY.md。
许可
MIT 许可证——请参阅 LICENSE。上游数据仍遵循上文数据来源与许可中列出的许可。
作者
Hayal Oezkan · github.com/malkreide
更新日志
请参阅 CHANGELOG.md。
安装
通过 uv 的 uvx 运行——无需克隆或手动安装。将其添加到 MCP 客户端配置中(Claude Desktop、Cursor 和 Windsurf 使用 mcpServers;VS Code 在 .vscode/mcp.json 中使用顶层 servers 键):
{
"mcpServers": {
"swiss-electricity-mcp": {
"command": "uvx",
"args": [
"swiss-electricity-mcp"
]
}
}
}
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。