{
  "markdown": "> 🇨🇭 **Part of the [Swiss Public Data MCP Portfolio](https://github.com/malkreide)**\n>\n> This is a **private project**. It is independent of any employer or institutional affiliation and represents no official position of any authority.\n\n# 📅 swiss-holidays-mcp\n\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n[![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)\n[![MCP](https://img.shields.io/badge/MCP-Model%20Context%20Protocol-purple)](https://modelcontextprotocol.io/)\n[![CI](https://github.com/malkreide/swiss-holidays-mcp/actions/workflows/ci.yml/badge.svg)](https://github.com/malkreide/swiss-holidays-mcp/actions)\n[![No Auth Required](https://img.shields.io/badge/auth-not%20required-lightgrey)](https://github.com/malkreide/swiss-holidays-mcp)\n[![Data Source](https://img.shields.io/badge/Data-OpenHolidays%20%2F%20Nager.Date-green)](https://www.openholidaysapi.org/)\n\n> 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.\n\n[🇩🇪 Deutsche Version](README.de.md)\n\n---\n\n## Overview\n\n**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.\n\nThe 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](https://www.openholidaysapi.org/) (CC BY 4.0) and [Nager.Date](https://date.nager.at/) (MIT).\n\n> **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.\n\n**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?\"*\n→ 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.\n→ [More use cases by audience](EXAMPLES.md) →\n\n### Demo\n\n![Demo: Claude using find_common_free_window and compare_school_holidays](docs/assets/demo.svg)\n\n---\n\n## Features\n\n- 🏫 **School holidays** — periods per canton and date range, differentiated by *Schulart* (`VS` / `MS` / `BS` / `EO`)\n- 🎌 **Public holidays** — cantonal holiday sets, not just the federal minimum (Berchtoldstag & friends)\n- 🔍 **Date check** — is a given date a school or public holiday in a canton?\n- 🔗 **Cross-cantonal comparison** — pairwise overlap matrix of holiday days between cantons\n- 🪟 **Common free windows** — date ranges where all listed cantons are simultaneously on holiday\n- 🌉 **Long weekends & bridge days** — computed from federal public holidays (Nager.Date)\n- 🏘️ **Local & municipal holidays** — district- and municipality-level specifics such as Zurich's Sechseläuten and Knabenschiessen, with a `scope` marker so they are never mistaken for canton-wide\n- 📆 **iCal / ICS export** — a canton's holidays for a year as a ready-to-import `.ics` calendar\n- 🔖 **Holiday feed resource** — `holidays://<canton>/<year>` MCP resource with a Markdown summary\n- 📌 **\"Is today a holiday?\"** — one-call convenience for the everyday question\n- 🩺 **Source health** — reachability and latency of both upstreams, always evaluable\n- 🔑 **No authentication required** — both data sources are publicly accessible\n- ☁️ **Dual transport** — stdio for Claude Desktop, Streamable HTTP/SSE for cloud deployment\n- 🧾 **Provenance on every response** — `live_api` | `cached` | `degraded`, never a silent empty list\n\n---\n\n## Data Sources\n\n| Source | Data | Licence |\n|---|---|---|\n| [OpenHolidays API](https://www.openholidaysapi.org/) | Cantons, *Schularten*, school holidays, public holidays | CC BY 4.0 |\n| [Nager.Date](https://date.nager.at/) | Long weekends and required bridge days | MIT |\n\nBoth sources are publicly accessible, no authentication required.\n**Attribution required:** OpenHolidays (CC BY 4.0) and Nager.Date must be cited as the source when using their data.\n\n---\n\n## Tools\n\n| Tool | Purpose | Data Source |\n|---|---|---|\n| `list_cantons` | The 26 cantons with ISO codes and official languages | OpenHolidays |\n| `list_school_types` | *Schulart* groups per canton (`CH-ZH-VS` etc.) | OpenHolidays |\n| `get_school_holidays` | School holidays for one canton and date range | OpenHolidays |\n| `get_public_holidays` | Public holidays for one canton and year | OpenHolidays |\n| `get_local_holidays` | Public holidays for one municipality or district, incl. local specifics | OpenHolidays |\n| `check_date` | Is a given date a school or public holiday? | OpenHolidays |\n| `compare_school_holidays` | Pairwise overlap matrix across cantons | OpenHolidays |\n| `find_common_free_window` | Windows where all listed cantons are on holiday | OpenHolidays |\n| `next_school_holidays` | The next upcoming holiday periods | OpenHolidays |\n| `get_long_weekends` | Long weekends and required bridge days | Nager.Date |\n| `export_holidays_ics` | A canton's holidays for a year as an iCalendar (`.ics`) document | OpenHolidays |\n| `is_holiday_today` | Is today a school or public holiday in a canton? | OpenHolidays |\n| `source_status` | Reachability and latency of both upstreams | Built-in |\n\n### Resources\n\n| Resource URI | Content |\n|---|---|\n| `holidays://{canton}/{year}` | Markdown summary of all public + school holidays, e.g. `holidays://CH-ZH/2026` |\n\nAll 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).\n\n### Example Use Cases\n\n| Query | Tool |\n|---|---|\n| *\"Which cantons are there, and what are their codes?\"* | `list_cantons` |\n| *\"Show Zurich's compulsory-school holidays for spring 2026\"* | `get_school_holidays` |\n| *\"Is 3 April 2026 a public holiday in Ticino?\"* | `check_date` |\n| *\"Do Zurich and Zug school holidays overlap this year?\"* | `compare_school_holidays` |\n| *\"When can all of ZH, ZG, AG plan a joint week off school?\"* | `find_common_free_window` |\n| *\"What are the next holidays for Basel-Stadt schools?\"* | `next_school_holidays` |\n| *\"Which long weekends does 2026 have, and which bridge days do they need?\"* | `get_long_weekends` |\n| *\"Which local holidays does the city of Zurich keep that the rest of the canton doesn't?\"* | `get_local_holidays` |\n| *\"Export Zurich's 2026 holidays as an .ics calendar I can import\"* | `export_holidays_ics` |\n| *\"Is today a holiday in Aargau?\"* | `is_holiday_today` |\n\n---\n\n## 🛡️ Safety & Limits\n\n| Aspect | Details |\n|--------|---------|\n| **Access** | Read-only (`readOnlyHint: true`) — the server cannot modify or delete any data |\n| **Personal data** | No personal data — all sources are aggregated, public holiday calendars |\n| **Caching** | 12-hour in-memory TTL (holiday tables change a handful of times per year) |\n| **Retry** | Exponential backoff 2s / 4s / 8s; 4xx except 429 are not retried |\n| **Timeout** | 20 seconds per API call (8 seconds for health probes) |\n| **Authentication** | No API keys required — both upstreams are publicly accessible |\n| **Degradation** | Upstream failure yields a `degraded` envelope with an explanatory `note`, never a silent empty list |\n| **Terms of Service** | Subject to the ToS of the respective data sources: [OpenHolidays](https://www.openholidaysapi.org/), [Nager.Date](https://date.nager.at/) |\n\n---\n\n## Architecture\n\nThis server uses **Architecture A (live API only, with in-memory cache)**.\n\n```\n                    ┌──────────────────────────┐\n   Claude / any ───▶│  swiss-holidays-mcp      │\n   MCP host         │  (MCPServer · 13 tools)  │\n                    └────────┬─────────────────┘\n                             │  retry 2s/4s/8s · 12h cache\n                    ┌────────┴─────────┐\n                    ▼                  ▼\n          OpenHolidays API        Nager.Date\n          (CC BY 4.0)             (MIT)\n          cantons · Schularten    long weekends\n          school + public         bridge days\n```\n\n**Rationale (verified live on 2026-07-19):**\n\n- All ten documented OpenHolidays endpoints answered HTTP 200 with plausible payloads; `/Subdivisions?countryIsoCode=CH` returns exactly 26 cantons, matching the official count.\n- No public bulk dump could be verified at build time (`openpotato/openholidays.data` raw access returned 404), so Architecture B was not available.\n- Holiday tables change a handful of times per year, so a 12-hour in-memory TTL removes almost all upstream load without risking staleness.\n\n**Consequences:**\n\n- Every response carries `provenance` (`live_api` | `cached` | `degraded`).\n- Upstream failure yields a `degraded` envelope with an explanatory `note`, never a silent empty list.\n- `source_status` always returns an evaluable health report.\n\n---\n\n## Live-probe findings (2026-07-19)\n\n| Endpoint | HTTP | Status | Records | Note |\n|---|---|---|---|---|\n| `/Countries` | 200 | ✅ works | 36 | |\n| `/Subdivisions?countryIsoCode=CH` | 200 | ✅ works | 26 | matches official canton count |\n| `/Groups?countryIsoCode=CH` | 200 | ✅ works | 11 | *Schulart* groups, only 6 cantons |\n| `/PublicHolidays` (CH, 2026) | 200 | ✅ works | 39 | cantonal scope included |\n| `/SchoolHolidays` (CH, 2026) | 200 | ✅ works | 193 | 183 distinct after school-type split |\n| `/SchoolHolidaysByDate` | 200 | ✅ works | – | |\n| `/SchoolHolidays?countryIsoCode=XX` | 200 | ⚠️ silently empty | 0 | invalid country ≠ error |\n| `/Subdivisions?languageIsoCode=ZZ` | 200 | ⚠️ silent EN fallback | 26 | invalid language ≠ error |\n| `/SchoolHolidays` without date range | 400 | ✅ correct error | – | RFC 9110 problem+json |\n| Nager `/PublicHolidays/2026/CH` | 200 | ✅ works | 33 | 29 rows carry `counties` |\n| Nager `/LongWeekend/2026/CH` | 200 | ✅ works | 3 | |\n| Nager `/PublicHolidays/2026/XX` | 404 | ✅ correct error | – | stricter than OpenHolidays |\n\n### Known findings\n\n1. **Apparent duplicates are school types.** Zurich returns *Frühlingsferien 2026* twice: once for `CH-ZH-VS` (Volksschulen, tagged `Recommended`) and once for `CH-ZH-BS` + `CH-ZH-MS` (Berufsfach- and Mittelschulen). Use the `school_type` parameter (`VS` / `MS` / `BS` / `EO`) rather than de-duplicating.\n2. **Only six cantons differentiate** by school type (AI, AR, BE, GR, SO, ZH). Elsewhere `groups` is absent and one table covers everything. The filter therefore treats an absent `groups` field as \"applies to all\".\n3. **Subdivision codes mix levels.** Records may carry `CH-AI-AP` or `CH-BE-TH-BL`. Always match on the `CH-XX` prefix, never on string equality.\n4. **An empty list is not an answer.** An unknown country or canton code yields HTTP 200 with `[]`. This server sets an explanatory `note` so that \"no holidays\" and \"bad filter\" stay distinguishable.\n\n---\n\n## Prerequisites\n\n- Python 3.10 or higher\n- [uv](https://docs.astral.sh/uv/) / uvx (recommended) or pip\n- Internet access (both APIs are publicly available)\n\n---\n\n## Installation\n\nRun via [`uv`](https://docs.astral.sh/uv/)'s `uvx` — no clone or manual install needed:\n\n```bash\nuvx swiss-holidays-mcp\n```\n\n### Development\n\n```bash\ngit clone https://github.com/malkreide/swiss-holidays-mcp\ncd swiss-holidays-mcp\npip install -e \".[dev]\"\n```\n\n---\n\n## Configuration\n\n### Claude Desktop\n\nAdd to `claude_desktop_config.json`:\n\n**macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json`\n**Windows:** `%APPDATA%\\Claude\\claude_desktop_config.json`\n\n```json\n{\n  \"mcpServers\": {\n    \"swiss-holidays\": {\n      \"command\": \"uvx\",\n      \"args\": [\"swiss-holidays-mcp\"]\n    }\n  }\n}\n```\n\nRestart Claude Desktop — the server starts automatically on first use.\n\n### Cloud Deployment (SSE / Streamable HTTP for browser access)\n\nFor use via **claude.ai in the browser** (e.g. on managed workstations without local software):\n\n```bash\nMCP_TRANSPORT=sse PORT=8000 python -m swiss_holidays_mcp\n```\n\nThe SDK exposes SSE at `/sse`, not `/mcp`.\n\n| Variable | Default | Description |\n|---|---|---|\n| `MCP_TRANSPORT` | `stdio` | Transport: `stdio`, `sse`, `streamable-http` (aka `http`) |\n| `PORT` / `MCP_PORT` | `8000` | Port for HTTP transports |\n| `MCP_HOST` | `127.0.0.1` | Bind address for HTTP transports. Loopback by default; `0.0.0.0` is opt-in and logs a warning — run behind an authenticating reverse proxy. |\n| `MCP_CORS_ORIGINS` | *(empty)* | Comma-separated extra CORS origins for browser clients (audit SDK-004). Loopback origins are always allowed; add the public origin your UI is served from, e.g. `https://ui.example.ch`. Never `*`. |\n\nThe HTTP transports attach an explicit CORS layer that exposes the\n`Mcp-Session-Id` header, so a browser MCP client can read the session id and\nmake follow-up requests. The allow-list is never a wildcard.\n\nRunning **more than one HTTP instance** behind a load balancer requires sticky\nsessions keyed on `Mcp-Session-Id` — see [`docs/scaling.md`](docs/scaling.md)\nfor nginx/Traefik/Kubernetes examples. A single instance (the common case) needs\nno affinity configuration.\n\n> 💡 *\"stdio for the developer laptop, SSE for the browser.\"*\n\n---\n\n## Project Structure\n\n```\nswiss-holidays-mcp/\n├── src/\n│   └── swiss_holidays_mcp/\n│       ├── __init__.py       # Package init\n│       ├── __main__.py       # Entry point: stdio / SSE / Streamable HTTP\n│       ├── server.py         # MCPServer: lifespan, 13 tools, 1 resource, op_* logic\n│       ├── client.py         # Shared HTTP client: retry, 12h cache, egress guard\n│       ├── guard.py          # Egress / SSRF guard (HTTPS + allow-list + IP blocklist)\n│       ├── pinning.py        # DNS-pinning transport (TOCTOU-free connect, SEC-005)\n│       ├── ical.py           # RFC 5545 iCalendar (.ics) writer\n│       ├── settings.py       # Pydantic-Settings config (loopback default)\n│       ├── logging_setup.py  # Structured logging to stderr\n│       ├── constants.py      # Canton codes, Schulart suffixes, API bases, allow-list\n│       └── models.py         # Pydantic v2 response envelopes\n├── tests/\n│   ├── conftest.py           # respx fixtures\n│   ├── test_tools.py         # Tool unit tests (mocked, no network)\n│   ├── test_resilience.py    # Degradation / retry / cache behaviour\n│   └── test_live.py          # Live smoke tests (marker: live)\n├── docs/                     # roadmap.md, security.md, network-egress.md\n├── deploy/                   # Network-layer egress manifests (Cilium / NetworkPolicy)\n├── audits/                   # mcp-audit run artifacts\n├── Dockerfile                # Non-root multi-stage container\n├── .github/\n│   ├── dependabot.yml        # Weekly dependency / action update PRs\n│   └── workflows/            # ci.yml, live-tests.yml, publish.yml\n├── pyproject.toml\n├── CHANGELOG.md\n├── CONTRIBUTING.md           # Contributing guide (English)\n├── CONTRIBUTING.de.md        # Contributing guide (German)\n├── SECURITY.md               # Security policy (English)\n├── SECURITY.de.md            # Security policy (German)\n├── EXAMPLES.md               # Use cases by audience\n├── server.json               # MCP registry manifest\n├── LICENSE\n├── README.md                 # This file (English)\n└── README.de.md              # German version\n```\n\n**On the single-file `server.py` (audit ARCH-011).** The 13 tools deliberately\nlive in one module rather than a `tools/` package. Each tool is a thin, uniform\nwrapper (`@mcp.tool` → `@_safe_tool` → `op_*`) over a transport-agnostic `op_*`\noperation, and every operation shares the same small set of helpers\n(`_to_period`, `_matches_school_type`, `_require_known_canton`, …) and the one\n`HolidayClient`. Splitting these across files would scatter that shared core and\nduplicate imports for no isolation benefit — the file is uniformly sectioned\n(aliases → helpers → `op_*` logic → tool wrappers → resource) and every `op_*`\nis unit-tested directly without a transport. A `tools/` split is the planned\nstep **only** if Phase 2 pushes the tool count materially higher.\n\n---\n\n## Lifecycle Phase\n\nThis server is in **Phase 1 (read-only)** — all tools read-only, no auth, no side\neffects. The 13-tool budget (of the 15–20 recommended maximum) still leaves\nheadroom. Local and municipal specifics — including Zurich's Sechseläuten and\nKnabenschiessen — are covered directly from OpenHolidays via `get_local_holidays`\n(a live probe showed they are published upstream at Gemeinde level), so no\nseparate city data source is required for them.\n\n---\n\n## MCP Primitives & Protocol Version\n\n- **Primitives — Tools + Resources.** The 13 tools are idempotent,\n  side-effect-free `GET`s. A **Resource** exposes a stable URI feed\n  (`holidays://<canton>/<year>`) so clients can read a canton's calendar as\n  cacheable context without a tool call. There are no recurring templated\n  workflows, so **Prompts** are not used (revisited if that changes).\n- **MCP protocol version — two eras.** `mcp` 2.x serves both over the same\n  server, and the client's first request on a connection decides which applies:\n  the `initialize` handshake caps at **`2025-11-25`**, the per-request envelope\n  reaches **`2026-07-28`**.\n\n  `source_status` surfaces one of them in its `mcp_protocol_version` field — a\n  single string cannot name both — and it surfaces the **handshake ceiling**,\n  because that is what a client reaching this server over `initialize` actually\n  negotiated. Measured, not inferred from a constant name: a client asking the\n  handshake for `2026-07-28` gets `2025-11-25` back.\n\n  `MCP_PROTOCOL_VERSION` is derived from the SDK's `LATEST_HANDSHAKE_VERSION`\n  rather than written down, so it cannot drift the way it once did — it stood\n  at `2025-06-18` for two revisions while every call reported it as fact.\n  [`tests/test_protocol_version.py`](tests/test_protocol_version.py) holds both\n  eras against the SDK and checks the delivered field against the SDK too, not\n  against the constant it came from.\n  The wire version is negotiated by the pinned `mcp` SDK (`mcp>=2.0.0,<3`).\n- **Update policy.** SDK and dependency bumps land via Dependabot (weekly);\n  protocol-version or tool-definition changes are recorded in\n  [`CHANGELOG.md`](CHANGELOG.md) with a version bump.\n\n## Data classification\n\nAll data is **Öffentlich / Public Open Data** — aggregated holiday calendars,\nno personal data (DSG/DSGVO). This is the highest classification the server\nhandles; the full model is in [`docs/security.md`](docs/security.md).\n\n## Known Limitations\n\n- **Unofficial source.** OpenHolidays aggregates cantonal publications. For legally binding dates, the cantonal authority remains authoritative. Every response says so.\n- **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 through `get_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.\n- **Nager long weekends ignore cantonal holidays.** They are computed from nationwide holidays only.\n- **No historical depth guarantee.** Coverage of years before roughly 2020 is uneven.\n\n---\n\n## Testing\n\n```bash\n# Unit tests (no network required — respx-mocked)\nPYTHONPATH=src pytest tests/ -m \"not live\"\n\n# Live smoke tests (hits the real upstream APIs)\nPYTHONPATH=src pytest tests/ -m \"live\"\n\n# Linting\nruff check src/ tests/ scripts/\nruff format --check src/ tests/ scripts/\n```\n\n---\n\n## Contributing\n\nContributions are welcome! Please read [CONTRIBUTING.md](CONTRIBUTING.md) (English) · [CONTRIBUTING.de.md](CONTRIBUTING.de.md) (German) for guidelines on reporting bugs, setting up the development environment, code style and test requirements.\n\nThis project follows the conventions of the [Swiss Public Data MCP Portfolio](https://github.com/malkreide).\n\n---\n\n## Security\n\nTo report a vulnerability, please follow the responsible disclosure process in [SECURITY.md](SECURITY.md) (English) · [SECURITY.de.md](SECURITY.de.md) (German). The server is read-only and requires no API key; see the *Safety & Limits* section above for the security model.\n\n---\n\n## Changelog\n\nSee [CHANGELOG.md](CHANGELOG.md)\n\n---\n\n## Deployment for Swiss Public Administration\n\nIf you self-host this server for a Swiss school authority or municipal use case:\n\n- **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.\n- **Upstream calls** go to OpenHolidays (EU-hosted OGD project) and Nager.Date. No personal data leaves your environment; only holiday calendars are requested.\n- **Logging:** logs are written to stderr; configure your IT retention policy accordingly.\n- **HTTP transport** should run behind a reverse proxy with authentication and per-IP rate limits — the server has no built-in authentication.\n\n---\n\n## License\n\nMIT License — see [LICENSE](LICENSE)\n\nSource 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.\n\n---\n\n## Author\n\nHayal Oezkan · [github.com/malkreide](https://github.com/malkreide)\n\n---\n\n## Credits & Related Projects\n\n- **Data:** [OpenHolidays API](https://www.openholidaysapi.org/) (CC BY 4.0) · [Nager.Date](https://date.nager.at/) (MIT)\n- **Protocol:** [Model Context Protocol](https://modelcontextprotocol.io/) — Anthropic / Linux Foundation\n- **Built following** the `mcp-data-source-probe` methodology: *live probe before design, dump fallback before API dependency, retry before defeatism.*\n- **Portfolio:** [Swiss Public Data MCP Portfolio](https://github.com/malkreide)\n\n| Server | Description |\n|--------|-------------|\n| [`zh-education-mcp`](https://github.com/malkreide/zh-education-mcp) | Canton of Zurich education data |\n| [`zurich-opendata-mcp`](https://github.com/malkreide/zurich-opendata-mcp) | City of Zurich Open Data |\n| [`swiss-statistics-mcp`](https://github.com/malkreide/swiss-statistics-mcp) | BFS STAT-TAB — Swiss federal statistics |\n| [`swisstopo-mcp`](https://github.com/malkreide/swisstopo-mcp) | Swiss federal geodata (swisstopo) |\n\nMIT licensed. Public money, public code.\n\n<!-- mcp-name: io.github.malkreide/swiss-holidays-mcp -->\n",
  "bytes": 23382,
  "sha": "d837121402eb27a972d9916c03daa47943f44ad8e4d01f3083298092ec83a8c4",
  "repo_slug": "malkreide/swiss-holidays-mcp",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_malkreide_swiss_holidays_mcp_b020430b/readme"
}