
Swiss Holidays Mcp
io.github.malkreide由 GitHub 账号 malkreide 发布v0.7.0更新于 Oct 24, 2026
Swiss school & public holidays, 26 cantons, by Schulart (OpenHolidays + Nager.Date)
概览
瑞士节假日日历 MCP 服务器:让助手查询全部 26 个州的公共假日与学校假期、比较各州假期并导出 iCal 日历。
- 可以做什么
- 列出 26 个州及其学校类型(Schulart)分组
- 按州和日期范围查询学校假期与公共假日,包括地方和市镇假日
- 判断某日期或今天是否为假日,并查询接下来的学校假期
- 两两比较各州假期,找出多个州同时放假的时段
- 计算长周末与桥接日,并把某州全年假日导出为 iCalendar 文档
- 以 Markdown 资源读取某州日历,并检查上游数据源健康状况
- 适用场景
- 适合围绕瑞士学校假期或公共假日做安排:跨州假期重叠、家庭或排班规划、桥接日,以及生成可导入的日历。可省去逐一打开 26 个州 PDF 日历的麻烦。
- 运行要求
- 本地 Python 进程,需 Python 3.10 或更高版本,通过 uvx(或 pip)运行。需要访问 OpenHolidays 与 Nager.Date 两个 API 的网络连接。无需 API 密钥或账号。可选的 HTTP/SSE 传输通过 MCP_TRANSPORT、PORT、MCP_HOST 和 MCP_CORS_ORIGINS 配置。
安装
在 SourceWeft 中
- 打开 控制台中的 Swiss Holidays Mcp,将其添加到工作区。
- 为需要使用其工具的对话启用该服务。
Desktop only,通过 STDIO。 STDIO 服务会启动本地进程,因此需要 SourceWeft 桌面宿主。
其他 MCP 客户端
参照 仓库 中的启动说明。
README
🇨🇭 Part of the Swiss Public Data MCP Portfolio
This is a private project. It is independent of any employer or institutional affiliation and represents no official position of any authority.
📅 swiss-holidays-mcp
[License: MIT] [Python 3.10+] [MCP] [CI] [No Auth Required] [Data Source]
A Swiss holiday calendar for AI agents — public holidays, school holidays and long weekends for all 26 cantons, with cross-cantonal comparison. School holidays are differentiated by Schulart (school type), which matters more than it first appears. No API key required.
Overview
swiss-holidays-mcp is a Swiss holiday calendar for AI assistants like Claude — public holidays, school holidays and long weekends for all 26 cantons, no API keys required. Public holidays are cantonal (Berchtoldstag, Fronleichnam & co. differ by canton, not just the federal minimum). School holidays are set cantonally, sometimes at district level, and — in six cantons — separately per school type. A single federal calendar does not exist; anyone planning across cantonal borders is otherwise reduced to opening 26 PDF pages.
The server covers two thematic clusters: public holidays / long weekends and school holidays (with Schulart differentiation). Each cluster maps to a group of purpose-built tools that translate raw agency data into clean, provenance-tagged JSON responses. All data comes from the OpenHolidays API (CC BY 4.0) and Nager.Date (MIT).
Mnemonic: A duplicate in Swiss school data is usually a school type in disguise. The underlying API publishes the same holiday period several times when a canton differentiates by school type. That looks like duplicated data and invites naive de-duplication — which would destroy exactly the distinction a school authority needs.
Anchor demo query: "In which weeks of 2026 are the compulsory schools of Zurich, Zug and Aargau simultaneously on holiday — and how many overlapping days does each pair share?"
→ This exercises find_common_free_window, compare_school_holidays and list_school_types in a single conversation, and answers a question that recurs every planning cycle in inter-cantonal coordination.
→ More use cases by audience →
Demo
[Demo: Claude using find_common_free_window and compare_school_holidays]
Features
- 🏫 School holidays — periods per canton and date range, differentiated by Schulart (
VS/MS/BS/EO) - 🎌 Public holidays — cantonal holiday sets, not just the federal minimum (Berchtoldstag & friends)
- 🔍 Date check — is a given date a school or public holiday in a canton?
- 🔗 Cross-cantonal comparison — pairwise overlap matrix of holiday days between cantons
- 🪟 Common free windows — date ranges where all listed cantons are simultaneously on holiday
- 🌉 Long weekends & bridge days — computed from federal public holidays (Nager.Date)
- 🏘️ Local & municipal holidays — district- and municipality-level specifics such as Zurich's Sechseläuten and Knabenschiessen, with a
scopemarker so they are never mistaken for canton-wide - 📆 iCal / ICS export — a canton's holidays for a year as a ready-to-import
.icscalendar - 🔖 Holiday feed resource —
holidays://<canton>/<year>MCP resource with a Markdown summary - 📌 "Is today a holiday?" — one-call convenience for the everyday question
- 🩺 Source health — reachability and latency of both upstreams, always evaluable
- 🔑 No authentication required — both data sources are publicly accessible
- ☁️ Dual transport — stdio for Claude Desktop, Streamable HTTP/SSE for cloud deployment
- 🧾 Provenance on every response —
live_api|cached|degraded, never a silent empty list
Data Sources
Both sources are publicly accessible, no authentication required. Attribution required: OpenHolidays (CC BY 4.0) and Nager.Date must be cited as the source when using their data.
Tools
Resources
All tools carry the full annotation set — readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true (they reach an external API). No tool writes anywhere. Inputs are schema-validated (canton codes against the 26 known cantons, dates as YYYY-MM-DD, year bounded, language/school_type whitelisted).
Example Use Cases
🛡️ Safety & Limits
Architecture
This server uses Architecture A (live API only, with in-memory cache).
Rationale (verified live on 2026-07-19):
- All ten documented OpenHolidays endpoints answered HTTP 200 with plausible payloads;
/Subdivisions?countryIsoCode=CHreturns exactly 26 cantons, matching the official count. - No public bulk dump could be verified at build time (
openpotato/openholidays.dataraw access returned 404), so Architecture B was not available. - Holiday tables change a handful of times per year, so a 12-hour in-memory TTL removes almost all upstream load without risking staleness.
Consequences:
- Every response carries
provenance(live_api|cached|degraded). - Upstream failure yields a
degradedenvelope with an explanatorynote, never a silent empty list. source_statusalways returns an evaluable health report.
Live-probe findings (2026-07-19)
Known findings
- Apparent duplicates are school types. Zurich returns Frühlingsferien 2026 twice: once for
CH-ZH-VS(Volksschulen, taggedRecommended) and once forCH-ZH-BS+CH-ZH-MS(Berufsfach- and Mittelschulen). Use theschool_typeparameter (VS/MS/BS/EO) rather than de-duplicating. - Only six cantons differentiate by school type (AI, AR, BE, GR, SO, ZH). Elsewhere
groupsis absent and one table covers everything. The filter therefore treats an absentgroupsfield as "applies to all". - Subdivision codes mix levels. Records may carry
CH-AI-APorCH-BE-TH-BL. Always match on theCH-XXprefix, never on string equality. - An empty list is not an answer. An unknown country or canton code yields HTTP 200 with
[]. This server sets an explanatorynoteso that "no holidays" and "bad filter" stay distinguishable.
Prerequisites
- Python 3.10 or higher
- uv / uvx (recommended) or pip
- Internet access (both APIs are publicly available)
Installation
Run via uv's uvx — no clone or manual install needed:
Development
Configuration
Claude Desktop
Add to claude_desktop_config.json:
macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
Windows: %APPDATA%\Claude\claude_desktop_config.json
Restart Claude Desktop — the server starts automatically on first use.
Cloud Deployment (SSE / Streamable HTTP for browser access)
For use via claude.ai in the browser (e.g. on managed workstations without local software):
The SDK exposes SSE at /sse, not /mcp.
The HTTP transports attach an explicit CORS layer that exposes the
Mcp-Session-Id header, so a browser MCP client can read the session id and
make follow-up requests. The allow-list is never a wildcard.
Running more than one HTTP instance behind a load balancer requires sticky
sessions keyed on Mcp-Session-Id — see docs/scaling.md
for nginx/Traefik/Kubernetes examples. A single instance (the common case) needs
no affinity configuration.
💡 "stdio for the developer laptop, SSE for the browser."
Project Structure
On the single-file server.py (audit ARCH-011). The 13 tools deliberately
live in one module rather than a tools/ package. Each tool is a thin, uniform
wrapper (@mcp.tool → @_safe_tool → op_*) over a transport-agnostic op_*
operation, and every operation shares the same small set of helpers
(_to_period, _matches_school_type, _require_known_canton, …) and the one
HolidayClient. Splitting these across files would scatter that shared core and
duplicate imports for no isolation benefit — the file is uniformly sectioned
(aliases → helpers → op_* logic → tool wrappers → resource) and every op_*
is unit-tested directly without a transport. A tools/ split is the planned
step only if Phase 2 pushes the tool count materially higher.
Lifecycle Phase
This server is in Phase 1 (read-only) — all tools read-only, no auth, no side
effects. The 13-tool budget (of the 15–20 recommended maximum) still leaves
headroom. Local and municipal specifics — including Zurich's Sechseläuten and
Knabenschiessen — are covered directly from OpenHolidays via get_local_holidays
(a live probe showed they are published upstream at Gemeinde level), so no
separate city data source is required for them.
MCP Primitives & Protocol Version
-
Primitives — Tools + Resources. The 13 tools are idempotent, side-effect-free
GETs. A Resource exposes a stable URI feed (holidays://<canton>/<year>) so clients can read a canton's calendar as cacheable context without a tool call. There are no recurring templated workflows, so Prompts are not used (revisited if that changes). -
MCP protocol version — two eras, both served natively.
mcp2.x serves both over the same server, and the client's first request on a connection decides which applies: theinitializehandshake caps at2025-11-25; the per-request envelope era (spec2026-07-28) has no handshake at all. There every request carries its protocol version, client info and client capabilities inparams._meta, mirrored by theMcp-Protocol-Version/Mcp-Method/Mcp-Namerouting headers, andserver/discovertakes the place ofinitialize.source_statusreports inmcp_protocol_versionthe revision this request came in on, read per request fromContext.protocol_version. It used to report one constant — the handshake ceiling — to every caller, so a client that had negotiated2026-07-28was told2025-11-25about its own connection. The constant survives only as the fallback for theop_*layer, where nothing was negotiated; it is derived from the SDK'sLATEST_HANDSHAKE_VERSIONrather than written down, so it cannot drift the way it once did — it stood at2025-06-18for two revisions while every call reported it as fact.Measured, not inferred:
tests/test_protocol_version.pydrives both eras through the same ASGI app this repo serves in production and compares the delivered field per era. The previous version of that file stated the repo could not build such an app — it can, and the claim outlived the measurement by two revisions. A client asking the handshake for2026-07-28still gets2025-11-25back, now measured here rather than at a sister server. The wire version is negotiated by the pinnedmcpSDK (mcp>=2.0.0,<3). -
Server identity travels with every result.
2026-07-28has noinitializeresult, soserverInforides along as a_metastamp on each one — the only place a caller learns which server and which build answered. The server handsMCPServerits package version and homepage; without them the SDK stampsversion: "", and it never substitutes one of its own. An empty version is worse than an absent one: it looks like an answer. -
Freshness hints on the listing methods (SEP-2549):
tools/list,resources/list,resources/templates/listandserver/discoveranswer withttlMs300000 andcacheScope: public. Without them the SDK's default is "stale immediately, never shared", which makes every client re-list on every connection — for lists that are fixed at import.resources/readcarries no hint:holidays://{canton}/{year}is a live query, not a directory. -
Update policy. SDK and dependency bumps land via Dependabot (monthly, grouped); protocol-version or tool-definition changes are recorded in
CHANGELOG.mdwith a version bump.
Data classification
All data is Öffentlich / Public Open Data — aggregated holiday calendars,
no personal data (DSG/DSGVO). This is the highest classification the server
handles; the full model is in docs/security.md.
Known Limitations
- Unofficial source. OpenHolidays aggregates cantonal publications. For legally binding dates, the cantonal authority remains authoritative. Every response says so.
- Municipal coverage depends on the upstream. OpenHolidays does carry district- and municipality-level public holidays (e.g. Sechseläuten, Knabenschiessen at
CH-ZH-ZH-ZH), exposed throughget_local_holidays. Completeness at Gemeinde level is only as good as the upstream data, which varies by canton. Municipal school holidays are not separately modelled. - Nager long weekends ignore cantonal holidays. They are computed from nationwide holidays only.
- No historical depth guarantee. Coverage of years before roughly 2020 is uneven.
Testing
Contributing
Contributions are welcome! Please read CONTRIBUTING.md (English) · CONTRIBUTING.de.md (German) for guidelines on reporting bugs, setting up the development environment, code style and test requirements.
This project follows the conventions of the Swiss Public Data MCP Portfolio.
Security
To report a vulnerability, please follow the responsible disclosure process in SECURITY.md (English) · SECURITY.de.md (German). The server is read-only and requires no API key; see the Safety & Limits section above for the security model.
Changelog
See CHANGELOG.md
Deployment for Swiss Public Administration
If you self-host this server for a Swiss school authority or municipal use case:
- Data residency: the query patterns themselves (which cantons a civil servant compares) may reveal ongoing planning and are best kept on Swiss or trusted infrastructure.
- Upstream calls go to OpenHolidays (EU-hosted OGD project) and Nager.Date. No personal data leaves your environment; only holiday calendars are requested.
- Logging: logs are written to stderr; configure your IT retention policy accordingly.
- HTTP transport should run behind a reverse proxy with authentication and per-IP rate limits — the server has no built-in authentication.
License
MIT License — see LICENSE
Source data is subject to the terms of OpenHolidays (CC BY 4.0) and Nager.Date (MIT); attribution to these sources is required when using their data.
Author
Hayal Oezkan · github.com/malkreide
Credits & Related Projects
- Data: OpenHolidays API (CC BY 4.0) · Nager.Date (MIT)
- Protocol: Model Context Protocol — Anthropic / Linux Foundation
- Built following the
mcp-data-source-probemethodology: live probe before design, dump fallback before API dependency, retry before defeatism. - Portfolio: Swiss Public Data MCP Portfolio
MIT licensed. Public money, public code.
来源:README.md,提交 4558879
工具
0版本历史
2- v0.7.0最新Sep 26, 2026
- v0.6.0Sep 16, 2026
评分
已满足适用检查项的 50%
该评分衡量此收录条目的完整程度,依据注册中心条目、端点与公开来源计算得出。它不是对服务器本身的评价,也不表示服务器安全或当前可用:可连通性是带日期的检查结果,评分也不包含安装量或用户评价。
- 近 30 天内可连通25 分
请保持端点能够响应 MCP 握手;SourceWeft 每 30 天探测一次。
- 已声明认证方式10 分
请在注册中心条目中声明凭据请求头,或保持端点能够响应探测,以便判定其认证方式。
- 已列出工具15 分
请让服务器在无凭据时响应 tools/list,或发布可在沙箱中无凭据启动的软件包。
- 描述必需15 分
- 图标10 分
- 文档15 分
- 许可证10 分