mcpField
S
swiss-holidays-mcp

swiss-holidays-mcp

本地服务
github.com生产力05 周前更新

关于 swiss-holidays-mcp

swiss-holidays-mcp 是面向 AI 助手的瑞士假期 MCP 服务,提供全部 26 个州的公共假期和学校假期数据。它按需区分学校类型,并支持跨州规划;数据来自无需 API 密钥的公开来源。

主要功能

  1. 01按州和学校类型查询学校假期
  2. 02查询州级及地方公共假期
  3. 03比较跨州假期重叠情况
  4. 04查询长周末和桥接日
  5. 05导出 iCalendar 并提供假期资源
  6. 06查看数据源状态与来源信息

适用场景

  1. 01查询某州的学校假期日期
  2. 02比较不同州的假期重叠情况
  3. 03为联合规划寻找共同假期
  4. 04将某州假期导出为 ICS 日历

MCP 相关

向 MCP 客户端提供只读假期工具和假期订阅资源,支持 stdio 与基于 HTTP 的传输方式。

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

mcp.json · 通用配置
{
"mcpServers": {
"malkreide-swiss-holidays": {
"command": "uvx",
"args": [
"swiss-holidays-mcp"
]
}
}
}
启动命令uvx swiss-holidays-mcp

本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。

安装包
pypiswiss-holidays-mcp0.7.0

工具

还没有获取到工具列表

本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。

详细介绍

🇨🇭 属于 瑞士公共数据 MCP 项目集

这是一个私人项目,与任何雇主或机构均无关联,也不代表任何主管部门的官方立场。

📅 swiss-holidays-mcp

License: MIT Python 3.10+ MCP CI No Auth Required Data Source

面向 AI Agent 的瑞士假日日历——涵盖全部 26 个州的公共假日、学校假期和长周末,并支持跨州比较。学校假期按 Schulart(学校类型)区分,这一点比乍看之下更重要。无需 API 密钥。

🇩🇪 德语版


概述

swiss-holidays-mcp 是一款面向 Claude 等 AI 助手的瑞士假日日历——涵盖全部 26 个州的公共假日、学校假期和长周末,无需 API 密钥。公共假日因州而异(Berchtoldstag、Fronleichnam 等节日各州安排不同,而不只是遵循联邦最低标准)。学校假期由各州设定,有时细化到地区级别;在六个州,还会按学校类型分别安排。瑞士并不存在统一的联邦日历;否则,任何跨州规划的人都只能逐一打开 26 个 PDF 页面。

该服务涵盖两个主题类别:公共假日 / 长周末和学校假期(按 Schulart 区分)。每个类别都对应一组专用工具,可将机构的原始数据转换为干净且带有来源信息的 JSON 响应。所有数据均来自 OpenHolidays API(CC BY 4.0)和 Nager.Date(MIT)。

**记忆要点:**瑞士学校数据中的重复项,往往暗藏着学校类型的区别。底层 API 会在某个州按学校类型区分假期时,多次发布同一假期时段。这看起来像是重复数据,也容易让人贸然去重——但这样会恰恰抹去学校主管部门需要的区别。

演示查询: “2026 年的哪些周里,苏黎世州、楚格州和阿尔高州的义务教育学校会同时放假——各州之间分别有多少天重叠?” → 该查询在一次对话中调用 find_common_free_window、compare_school_holidays 和 list_school_types,并回答了每个跨州协调规划周期都会遇到的问题。 → 按受众查看更多使用场景 →

演示

演示:Claude 使用 find_common_free_window 和 compare_school_holidays


功能

  • 🏫 学校假期 — 按州和日期范围列出时段,并按 Schulart(VS / MS / BS / EO)区分
  • 🎌 公共假日 — 按州列出假日,不仅限于联邦最低标准(包括 Berchtoldstag 等)
  • 🔍 日期查询 — 查询指定日期是否为某个州的学校假期或公共假日
  • 🔗 跨州比较 — 各州假期天数的成对重叠矩阵
  • 🪟 共同假期时段 — 所列各州同时放假的日期范围
  • 🌉 长周末和桥接假日 — 根据联邦公共假日计算(Nager.Date)
  • 🏘️ 地方和市镇假日 — 区级和市镇级的特定假日,例如苏黎世的 Sechseläuten 和 Knabenschiessen,并附带 scope 标记,以免误认为是全州假日
  • 📆 iCal / ICS 导出 — 将某州某年的假日导出为可直接导入的 .ics 日历
  • 🔖 假日源资源 — holidays://<canton>/<year> MCP 资源,附带 Markdown 摘要
  • 📌 “今天是假日吗?” — 一次调用即可回答日常问题
  • 🩺 来源状态 — 可随时检查两个上游来源的可访问性和延迟
  • 🔑 无需身份验证 — 两个数据源均可公开访问
  • ☁️ 双传输方式 — stdio 适用于 Claude Desktop,Streamable HTTP/SSE 适用于云端部署
  • 🧾 每次响应均注明来源 — live_api | cached | degraded,绝不会悄然返回空列表

数据源

来源数据许可证
OpenHolidays API各州、Schularten、学校假期、公共假日CC BY 4.0
Nager.Date长周末和必要的桥接假日MIT

两个数据源均可公开访问,无需身份验证。 必须注明来源: 使用其数据时,必须将 OpenHolidays(CC BY 4.0)和 Nager.Date 列为来源。


工具

工具用途数据源
list_cantons列出 26 个州及其 ISO 代码和官方语言OpenHolidays
list_school_types按州列出 Schulart 分组(如 CH-ZH-VS)OpenHolidays
get_school_holidays查询某个州和日期范围内的学校假期OpenHolidays
get_public_holidays查询某个州某一年的公共假日OpenHolidays
get_local_holidays查询某个市镇或区的公共假日,包括地方特定假日OpenHolidays
check_date查询指定日期是否为学校假期或公共假日OpenHolidays
compare_school_holidays各州学校假期的成对重叠矩阵OpenHolidays
find_common_free_window查找所列各州同时放假的时段OpenHolidays
next_school_holidays查询接下来即将到来的假期时段OpenHolidays
get_long_weekends查询长周末和必要的桥接假日Nager.Date
export_holidays_ics将某州某年的假日导出为 iCalendar(.ics)文档OpenHolidays
is_holiday_today查询今天是否为某个州的学校假期或公共假日OpenHolidays
source_status检查两个上游来源的可访问性和延迟内置
资源
资源 URI内容
holidays://{canton}/{year}所有公共假日和学校假日的 Markdown 摘要,例如 holidays://CH-ZH/2026

所有工具均带有完整的注释集——readOnlyHint: true、destructiveHint: false、idempotentHint: true、openWorldHint: true(会访问外部 API)。没有任何工具会写入数据。输入均经过架构验证(州代码必须属于已知的 26 个州,日期格式为 YYYY-MM-DD,year 限定在指定范围内,language/school_type 仅允许使用白名单中的值)。

示例用例
查询工具
“有哪些州,它们的代码是什么?”list_cantons
“显示苏黎世州 2026 年春季的义务教育假期”get_school_holidays
“2026 年 4 月 3 日是提契诺州的公共假日吗?”check_date
“今年苏黎世州和楚格州的学校假期有重叠吗?”compare_school_holidays
“ZH、ZG、AG 何时可以共同安排一周学校假期?”find_common_free_window
“巴塞尔城市州学校接下来的假期有哪些?”next_school_holidays
“2026 年有哪些长周末,需要搭配哪些桥接日?”get_long_weekends
“苏黎世市有哪些本地假日,而州内其他地方没有?”get_local_holidays
“将苏黎世州 2026 年的假日导出为可导入的 .ics 日历”export_holidays_ics
“今天阿尔高州放假吗?”is_holiday_today

🛡️ 安全与限制

方面详情
访问权限只读(readOnlyHint: true)——服务无法修改或删除任何数据
个人数据不涉及个人数据——所有数据源均为汇总后的公共假日日历
缓存12 小时内存 TTL(假日表每年只会更改少数几次)
重试指数退避 2s / 4s / 8s;除 429 外的 4xx 错误不会重试
超时每次 API 调用 20 秒(健康探测为 8 秒)
身份验证无需 API 密钥——两个上游服务均可公开访问
降级上游故障时会返回带有说明性 note 的 degraded 封装,绝不会静默返回空列表
服务条款受相应数据源的服务条款约束:OpenHolidays、Nager.Date

架构

此服务采用 架构 A(仅实时 API,使用内存缓存)。

                    ┌──────────────────────────┐
   Claude / any ───▶│  swiss-holidays-mcp      │
   MCP host         │  (MCPServer · 13 tools)  │
                    └────────┬─────────────────┘
                             │  retry 2s/4s/8s · 12h cache
                    ┌────────┴─────────┐
                    ▼                  ▼
          OpenHolidays API        Nager.Date
          (CC BY 4.0)             (MIT)
          cantons · Schularten    long weekends
          school + public         bridge days

理由(于 2026-07-19 实时验证):

  • 所有十个已记录的 OpenHolidays 端点均返回 HTTP 200,且响应内容合理;/Subdivisions?countryIsoCode=CH 恰好返回 26 个州,与官方数量一致。
  • 构建时无法验证公开的批量数据转储(openpotato/openholidays.data 的原始访问返回 404),因此无法采用架构 B。
  • 假日表每年只会更改少数几次,因此 12 小时的内存 TTL 几乎可以消除所有上游负载,同时避免数据过时。

影响:

  • 每个响应都包含 provenance(live_api | cached | degraded)。
  • 上游故障时会返回带有说明性 note 的 degraded 封装,绝不会静默返回空列表。
  • source_status 始终返回可评估的健康报告。

实时探测结果(2026-07-19)

端点HTTP状态记录数备注
/Countries200✅ 正常36
/Subdivisions?countryIsoCode=CH200✅ 正常26与官方州数量一致
/Groups?countryIsoCode=CH200✅ 正常11Schulart 分组,仅涵盖 6 个州
/PublicHolidays(CH,2026)200✅ 正常39包含州级范围
/SchoolHolidays(CH,2026)200✅ 正常193按学校类型拆分后有 183 个不同记录
/SchoolHolidaysByDate200✅ 正常–
/SchoolHolidays?countryIsoCode=XX200⚠️ 静默返回空结果0无效国家 ≠ 错误
/Subdivisions?languageIsoCode=ZZ200⚠️ 静默回退到英语26无效语言 ≠ 错误
/SchoolHolidays(未指定日期范围)400✅ 正确报错–RFC 9110 problem+json
Nager /PublicHolidays/2026/CH200✅ 正常3329 条记录包含 counties
Nager /LongWeekend/2026/CH200✅ 正常3
Nager /PublicHolidays/2026/XX404✅ 正确报错–校验比 OpenHolidays 更严格
已知发现
  1. 看似重复的记录其实对应不同学校类型。 Zurich 会返回两条 Frühlingsferien 2026:一条对应 CH-ZH-VS(Volksschulen,标记为 Recommended),另一条对应 CH-ZH-BS + CH-ZH-MS(Berufsfach- 和 Mittelschulen)。请使用 school_type 参数(VS / MS / BS / EO),不要进行去重。
  2. 只有六个州会按学校类型区分(AI、AR、BE、GR、SO、ZH)。其他地区不包含 groups,一张表涵盖所有学校。因此,筛选器会将缺少 groups 字段视为“适用于所有学校”。
  3. 细分区域代码的层级并不一致。 记录可能包含 CH-AI-AP 或 CH-BE-TH-BL。请始终按 CH-XX 前缀匹配,切勿进行字符串全等匹配。
  4. 空列表不等于有效答案。 未知的国家或州代码会返回 HTTP 200 和 []。此服务会设置说明性 note,以区分“没有假期”和“筛选条件无效”。

前置条件

  • Python 3.10 或更高版本
  • uv / uvx(推荐)或 pip
  • 可访问互联网(两个 API 均可公开访问)

安装

通过 uv 的 uvx 运行——无需克隆或手动安装:

uvx swiss-holidays-mcp
开发
git clone https://github.com/malkreide/swiss-holidays-mcp
cd swiss-holidays-mcp
pip install -e ".[dev]"

配置

Claude Desktop

添加到 claude_desktop_config.json:

macOS: ~/Library/Application Support/Claude/claude_desktop_config.json Windows: %APPDATA%\Claude\claude_desktop_config.json

{
  "mcpServers": {
    "swiss-holidays": {
      "command": "uvx",
      "args": ["swiss-holidays-mcp"]
    }
  }
}

重启 Claude Desktop——服务会在首次使用时自动启动。

云端部署(SSE / Streamable HTTP,供浏览器访问)

通过浏览器中的 claude.ai 使用时(例如,在无法安装本地软件的受管工作站上):

MCP_TRANSPORT=sse PORT=8000 python -m swiss_holidays_mcp

SDK 在 /sse 而非 /mcp 提供 SSE。

变量默认值说明
MCP_TRANSPORTstdio传输方式:stdio、sse、streamable-http(又称 http)
PORT / MCP_PORT8000HTTP 传输使用的端口
MCP_HOST127.0.0.1HTTP 传输的绑定地址。默认仅绑定环回地址;选择 0.0.0.0 时会记录警告——请在需要身份验证的反向代理后运行。
MCP_CORS_ORIGINS(空)浏览器客户端的额外 CORS 来源,以逗号分隔(审计 SDK-004)。始终允许环回来源;请添加 UI 所在的公开来源,例如 https://ui.example.ch。切勿使用 *。

HTTP 传输会添加一个显式的 CORS 层,公开 Mcp-Session-Id 标头,以便浏览器 MCP 客户端读取会话 ID 并 发送后续请求。允许列表绝不会使用通配符。

在负载均衡器后运行多个 HTTP 实例时,需要根据 Mcp-Session-Id 保持会话粘性——nginx/Traefik/Kubernetes 示例请参阅 docs/scaling.md。 单个实例(最常见的情况)无需配置会话亲和性。

💡 “开发者笔记本用 stdio,浏览器用 SSE。”


项目结构

swiss-holidays-mcp/
├── src/
│   └── swiss_holidays_mcp/
│       ├── __init__.py       # Package init
│       ├── __main__.py       # Entry point: stdio / SSE / Streamable HTTP
│       ├── server.py         # MCPServer: lifespan, 13 tools, 1 resource, op_* logic
│       ├── client.py         # Shared HTTP client: retry, 12h cache, egress guard
│       ├── guard.py          # Egress / SSRF guard (HTTPS + allow-list + IP blocklist)
│       ├── pinning.py        # DNS-pinning transport (TOCTOU-free connect, SEC-005)
│       ├── ical.py           # RFC 5545 iCalendar (.ics) writer
│       ├── settings.py       # Pydantic-Settings config (loopback default)
│       ├── logging_setup.py  # Structured logging to stderr
│       ├── constants.py      # Canton codes, Schulart suffixes, API bases, allow-list
│       └── models.py         # Pydantic v2 response envelopes
├── tests/
│   ├── conftest.py           # respx fixtures
│   ├── test_tools.py         # Tool unit tests (mocked, no network)
│   ├── test_resilience.py    # Degradation / retry / cache behaviour
│   └── test_live.py          # Live smoke tests (marker: live)
├── docs/                     # roadmap.md, security.md, network-egress.md
├── deploy/                   # Network-layer egress manifests (Cilium / NetworkPolicy)
├── audits/                   # mcp-audit run artifacts
├── Dockerfile                # Non-root multi-stage container
├── .github/
│   ├── dependabot.yml        # Monthly dependency / action update PRs
│   └── workflows/            # ci.yml, live-tests.yml, publish.yml
├── pyproject.toml
├── CHANGELOG.md
├── CONTRIBUTING.md           # Contributing guide (English)
├── CONTRIBUTING.de.md        # Contributing guide (German)
├── SECURITY.md               # Security policy (English)
├── SECURITY.de.md            # Security policy (German)
├── EXAMPLES.md               # Use cases by audience
├── server.json               # MCP registry manifest
├── LICENSE
├── README.md                 # This file (English)
└── README.de.md              # German version

关于单文件 server.py(审计 ARCH-011)。 这 13 个工具有意放在一个模块中,而不是拆分到 tools/ 包中。每个工具都是一个精简、统一的封装(@mcp.tool → @_safe_tool → op_*),位于与传输方式无关的 op_* 操作之上;所有操作共用一小组辅助函数(_to_period、_matches_school_type、_require_known_canton 等)和唯一的 HolidayClient。将这些内容拆分到多个文件会分散这部分共用核心,并重复导入,却无法带来隔离上的好处——文件按统一的区块组织(别名 → 辅助函数 → op_* 逻辑 → 工具封装 → 资源),而且每个 op_* 都会在不经过传输层的情况下直接进行单元测试。只有在 Phase 2 大幅增加工具数量时,才计划将其拆分到 tools/。


生命周期阶段

此服务器处于阶段 1(只读)——所有工具均为只读,无身份验证,也无副作用。13 个工具的预算(低于建议的 15–20 个上限)仍有余量。苏黎世的 Sechseläuten 和 Knabenschiessen 等本地及市镇级信息,均可直接通过 OpenHolidays 的 get_local_holidays 获取(实时探测显示,上游已在 Gemeinde 级别发布这些信息),因此无需单独的城市数据源。


MCP 原语与协议版本

  • 原语——工具 + 资源。 这 13 个工具均为幂等、无副作用的 GET。资源提供稳定的 URI 数据流(holidays://<canton>/<year>),客户端无需调用工具,即可将某州的日历作为可缓存的上下文读取。没有重复使用的模板化工作流,因此不使用提示词(如有变化,将重新评估)。

  • MCP 协议版本——两个时代,均由原生方式支持。 mcp 2.x 通过同一服务器同时提供两种版本,具体适用哪种版本由客户端在连接上的首个请求决定:initialize 握手最高支持 2025-11-25;按请求封装的时代(规范 2026-07-28)则完全没有握手。在该模式下,每个请求都会在 params._meta 中携带其协议版本、客户端信息和客户端能力,并通过 Mcp-Protocol-Version / Mcp-Method / Mcp-Name 路由标头镜像传递;server/discover 则取代 initialize。

    source_status 在 mcp_protocol_version 中报告此请求所用的版本,并逐个请求从 Context.protocol_version 读取。过去,它会向所有调用方报告同一个常量——握手版本上限,因此协商了 2026-07-28 的客户端会被告知其连接使用的是 2025-11-25。该常量仅作为 op_* 层的后备值保留,因为该层没有进行协商;它现在取自 SDK 的 LATEST_HANDSHAKE_VERSION,而不是硬编码,因此不会再像过去那样与实际情况脱节——曾有两个版本期间,它一直是 2025-06-18,每次调用却都将其报告为事实。

    这是实测结果,而非推断:tests/test_protocol_version.py 通过本仓库在生产环境中提供的同一个 ASGI 应用测试两个时代,并按时代比较实际返回的字段。该文件的上一版本曾声称本仓库无法构建此类应用——实际上可以;这一说法在实测后又沿用了两个版本。客户端在握手中请求 2026-07-28,仍会收到 2025-11-25;如今这一点已在此处实测,而非在关联服务器上测得。线上版本由固定版本的 mcp SDK(mcp>=2.0.0,<3)协商。

  • 每个结果都会携带服务器身份信息。 2026-07-28 没有 initialize 结果,因此 serverInfo 会作为 _meta 标记附加在每个结果上——这是调用方了解由哪台服务器、哪个构建版本提供响应的唯一途径。服务器会向 MCPServer 提供其软件包版本和主页;若不提供,SDK 会将版本标记为 version: "",且不会用自身的版本替代。空版本比没有版本更糟糕:它看起来像是一个有效答案。

  • 列表方法提供新鲜度提示(SEP-2549):tools/list、resources/list、resources/templates/list 和 server/discover 的响应包含 ttlMs 300000 和 cacheScope: public。没有这些提示时,SDK 的默认行为是“立即过期,绝不共享”,导致每个客户端每次连接时都重新获取列表——而这些列表在导入时就已固定。resources/read 不携带提示:holidays://{canton}/{year} 是实时查询,而非目录。

  • 更新策略。 SDK 和依赖项的升级通过 Dependabot 提交(每月一次,分组处理);协议版本或工具定义变更会记录在 CHANGELOG.md 中,并同时升级版本号。

数据分类

所有数据均为 Öffentlich / Public Open Data——汇总的节日日历,不含个人数据(DSG/DSGVO)。这是此服务器处理的最高分类;完整模型见 docs/security.md。

已知限制

  • 非官方来源。 OpenHolidays 汇总各州发布的信息。对于具有法律约束力的日期,应以州主管部门的信息为准。每次响应都会说明这一点。
  • 市镇级覆盖范围取决于上游数据。 OpenHolidays 确实提供区级和市镇级公共假日(例如 CH-ZH-ZH-ZH 的 Sechseläuten、Knabenschiessen),并通过 get_local_holidays 提供。这些数据在 Gemeinde 级别的完整性取决于上游数据的质量,而各州的数据质量不尽相同。市镇级学校假期不会单独建模。
  • Nager 的长周末计算不考虑各州假日。 计算仅基于全国性假日。
  • 不保证历史数据的覆盖深度。 约 2020 年以前的年份,数据覆盖并不均衡。

测试

# Unit tests (no network required — respx-mocked)
PYTHONPATH=src pytest tests/ -m "not live"

# Live smoke tests (hits the real upstream APIs)
PYTHONPATH=src pytest tests/ -m "live"

# Linting
ruff check src/ tests/ scripts/
ruff format --check src/ tests/ scripts/

贡献

欢迎贡献!请阅读 CONTRIBUTING.md(英语)· CONTRIBUTING.de.md(德语),了解如何报告错误、设置开发环境、遵循代码风格和满足测试要求。

本项目遵循 Swiss Public Data MCP Portfolio 的惯例。


安全

如需报告漏洞,请遵循 SECURITY.md(英语)· SECURITY.de.md(德语)中的负责任披露流程。该服务为只读服务,无需 API 密钥;有关安全模型,请参阅上文的安全与限制部分。


更新日志

请参阅 CHANGELOG.md


面向瑞士公共行政部门的部署

如果你为瑞士学校主管部门或市镇使用场景自托管此服务:

  • 数据驻留: 查询模式本身(公务人员正在比较哪些州)可能会泄露正在进行的规划,因此最好将其保留在瑞士或可信的基础设施上。
  • 上游调用会发送至 OpenHolidays(托管于欧盟的 OGD 项目)和 Nager.Date。不会有个人数据离开你的环境;仅请求假日历。
  • 日志记录: 日志写入 stderr;请据此配置 IT 保留策略。
  • HTTP 传输应在带有身份验证和按 IP 限速的反向代理后运行——该服务没有内置身份验证。

许可证

MIT License — 参见 LICENSE

源数据受 OpenHolidays (CC BY 4.0) 和 Nager.Date (MIT) 的条款约束;使用其数据时必须注明来源。


作者

Hayal Oezkan · github.com/malkreide


致谢与相关项目

服务描述
zh-education-mcp苏黎世州教育数据
zurich-opendata-mcp苏黎世市开放数据
swiss-statistics-mcpBFS STAT-TAB — 瑞士联邦统计数据
swisstopo-mcp瑞士联邦地理数据(swisstopo)

采用 MIT 许可。公共资金,公共代码。

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。