ferien-mcp 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ferien_mcp-0.1.0/.github/workflows/publish.yml +20 -0
- ferien_mcp-0.1.0/.github/workflows/registry.yml +95 -0
- ferien_mcp-0.1.0/.github/workflows/smoke.yml +21 -0
- ferien_mcp-0.1.0/LICENSE +21 -0
- ferien_mcp-0.1.0/PKG-INFO +71 -0
- ferien_mcp-0.1.0/README-de.md +59 -0
- ferien_mcp-0.1.0/README-fa.md +59 -0
- ferien_mcp-0.1.0/README.md +59 -0
- ferien_mcp-0.1.0/pyproject.toml +22 -0
- ferien_mcp-0.1.0/server.json +1 -0
- ferien_mcp-0.1.0/src/ferien_mcp/__init__.py +2 -0
- ferien_mcp-0.1.0/src/ferien_mcp/domain.py +257 -0
- ferien_mcp-0.1.0/src/ferien_mcp/http.py +115 -0
- ferien_mcp-0.1.0/src/ferien_mcp/server.py +188 -0
- ferien_mcp-0.1.0/tests/__init__.py +0 -0
- ferien_mcp-0.1.0/tests/fixtures/holidays-2025-be.json +1 -0
- ferien_mcp-0.1.0/tests/fixtures/holidays-2026-be.json +1 -0
- ferien_mcp-0.1.0/tests/fixtures/holidays-2026-by.json +1 -0
- ferien_mcp-0.1.0/tests/fixtures/holidays-2026-nw.json +1 -0
- ferien_mcp-0.1.0/tests/fixtures/holidays-2030-by.json +1 -0
- ferien_mcp-0.1.0/tests/fixtures/holidays-invalid-state.json +1 -0
- ferien_mcp-0.1.0/tests/smoke_live.py +17 -0
- ferien_mcp-0.1.0/tests/test_domain.py +217 -0
- ferien_mcp-0.1.0/tests/test_live.py +106 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags: ["v*"]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
publish:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
permissions:
|
|
11
|
+
id-token: write # required for PyPI trusted publishing (OIDC)
|
|
12
|
+
contents: read
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@v4
|
|
15
|
+
- uses: actions/setup-python@v5
|
|
16
|
+
with:
|
|
17
|
+
python-version: "3.12"
|
|
18
|
+
- run: pip install build
|
|
19
|
+
- run: python -m build
|
|
20
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
name: Publish to MCP Registry
|
|
2
|
+
|
|
3
|
+
# Official MCP Registry (registry.modelcontextprotocol.io) via GitHub OIDC.
|
|
4
|
+
# No secret, no interactive login: the runner's OIDC token proves ownership
|
|
5
|
+
# of the io.github.mohammad-abbasi-sh/* namespace.
|
|
6
|
+
# Refs: https://github.com/modelcontextprotocol/registry
|
|
7
|
+
|
|
8
|
+
on:
|
|
9
|
+
push:
|
|
10
|
+
tags: ["v*"]
|
|
11
|
+
workflow_dispatch:
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
publish:
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
permissions:
|
|
17
|
+
id-token: write # required for OIDC -> MCP Registry
|
|
18
|
+
contents: read
|
|
19
|
+
steps:
|
|
20
|
+
- uses: actions/checkout@v4
|
|
21
|
+
|
|
22
|
+
- name: Sync server.json version to tag
|
|
23
|
+
# Only sync when running on a version tag (v*). On workflow_dispatch
|
|
24
|
+
# (ref = refs/heads/main) keep the version committed in server.json.
|
|
25
|
+
run: |
|
|
26
|
+
VER=""
|
|
27
|
+
if [[ "$GITHUB_REF" == refs/tags/* ]]; then
|
|
28
|
+
TAG="${GITHUB_REF#refs/tags/}"
|
|
29
|
+
VER="${TAG#v}"
|
|
30
|
+
fi
|
|
31
|
+
if [ -n "$VER" ]; then
|
|
32
|
+
python3 - "$VER" << 'EOF'
|
|
33
|
+
import json, sys
|
|
34
|
+
ver = sys.argv[1]
|
|
35
|
+
d = json.load(open('server.json'))
|
|
36
|
+
d['version'] = ver
|
|
37
|
+
for pkg in d.get('packages', []):
|
|
38
|
+
pkg['version'] = ver
|
|
39
|
+
json.dump(d, open('server.json', 'w'), indent=2, ensure_ascii=False)
|
|
40
|
+
print('server.json version ->', ver)
|
|
41
|
+
EOF
|
|
42
|
+
else
|
|
43
|
+
echo "No version tag ($GITHUB_REF); keeping server.json version as-is"
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
- name: Wait for PyPI
|
|
47
|
+
# The registry validates that the PyPI package + version exists,
|
|
48
|
+
# but the PyPI publish workflow runs in parallel with this one.
|
|
49
|
+
# Poll until the version appears instead of racing it: a 400 here
|
|
50
|
+
# used to fail silently under continue-on-error and left 40/43
|
|
51
|
+
# servers unlisted in the registry (2026-11-11).
|
|
52
|
+
run: |
|
|
53
|
+
python3 - << 'EOF'
|
|
54
|
+
import json, sys, time, urllib.request, urllib.error
|
|
55
|
+
d = json.load(open('server.json'))
|
|
56
|
+
pkg = d['packages'][0]
|
|
57
|
+
name = pkg.get('identifier') or pkg.get('name')
|
|
58
|
+
ver = pkg['version']
|
|
59
|
+
if not name:
|
|
60
|
+
print('ERROR: no package identifier/name in server.json')
|
|
61
|
+
sys.exit(1)
|
|
62
|
+
url = f'https://pypi.org/pypi/{name}/json'
|
|
63
|
+
for attempt in range(1, 21):
|
|
64
|
+
try:
|
|
65
|
+
with urllib.request.urlopen(url, timeout=30) as r:
|
|
66
|
+
data = json.load(r)
|
|
67
|
+
if ver in data.get('releases', {}):
|
|
68
|
+
print(f'PyPI {name}=={ver} is live (attempt {attempt})')
|
|
69
|
+
sys.exit(0)
|
|
70
|
+
except urllib.error.HTTPError as e:
|
|
71
|
+
print(f'attempt {attempt}: PyPI returned {e.code}')
|
|
72
|
+
except Exception as e:
|
|
73
|
+
print(f'attempt {attempt}: {e}')
|
|
74
|
+
print(f'attempt {attempt}: {name}=={ver} not on PyPI yet, waiting 30s...')
|
|
75
|
+
time.sleep(30)
|
|
76
|
+
print(f'ERROR: {name}=={ver} did not appear on PyPI after 10 minutes')
|
|
77
|
+
sys.exit(1)
|
|
78
|
+
EOF
|
|
79
|
+
|
|
80
|
+
- name: Install mcp-publisher
|
|
81
|
+
run: |
|
|
82
|
+
curl -sL "https://github.com/modelcontextprotocol/registry/releases/download/v1.8.7/mcp-publisher_linux_amd64.tar.gz" -o /tmp/mcp-publisher.tar.gz
|
|
83
|
+
tar -xzf /tmp/mcp-publisher.tar.gz -C /tmp
|
|
84
|
+
sudo mv /tmp/mcp-publisher /usr/local/bin/mcp-publisher
|
|
85
|
+
mcp-publisher --version
|
|
86
|
+
|
|
87
|
+
- name: Validate server.json
|
|
88
|
+
run: mcp-publisher validate server.json
|
|
89
|
+
|
|
90
|
+
- name: Publish to MCP Registry (OIDC)
|
|
91
|
+
# Failures must fail the run visibly: a silent publish failure
|
|
92
|
+
# once left 40/43 servers unlisted in the registry (2026-11-11).
|
|
93
|
+
run: |
|
|
94
|
+
mcp-publisher login github-oidc
|
|
95
|
+
mcp-publisher publish server.json
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
name: Daily live smoke
|
|
2
|
+
|
|
3
|
+
# One fixed probe per day: catches fingerprint rot / bot-wall changes
|
|
4
|
+
# before users hit them. Fails visibly on 403/timeout.
|
|
5
|
+
on:
|
|
6
|
+
schedule:
|
|
7
|
+
- cron: "0 6 * * *"
|
|
8
|
+
workflow_dispatch:
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
smoke:
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@v4
|
|
15
|
+
- uses: actions/setup-python@v5
|
|
16
|
+
with:
|
|
17
|
+
python-version: "3.12"
|
|
18
|
+
- name: Install
|
|
19
|
+
run: pip install -e .
|
|
20
|
+
- name: Smoke probe
|
|
21
|
+
run: python tests/smoke_live.py
|
ferien_mcp-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Mohammad Abbasi
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: ferien-mcp
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Unofficial read-only MCP server for German school holidays (Schulferien) — holidays by year & state, is-it-a-holiday, next holiday & date ranges
|
|
5
|
+
Author: Mohammad Abbasi
|
|
6
|
+
License: MIT
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Requires-Python: >=3.10
|
|
9
|
+
Requires-Dist: httpx>=0.27
|
|
10
|
+
Requires-Dist: mcp<2
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
|
|
13
|
+
# ferien-mcp 🏖️
|
|
14
|
+
|
|
15
|
+
mcp-name: io.github.mohammad-abbasi-sh/ferien-mcp
|
|
16
|
+
|
|
17
|
+
Unofficial **read-only** MCP server for **German school holidays** (Schulferien) via the [ferien-api.de API](https://ferien-api.de) (bundesAPI project): holidays per year and federal state, is-it-a-holiday checks, the next holiday from a date, and holidays in a date range. No key, no login.
|
|
18
|
+
|
|
19
|
+
> Built by Mohammad Abbasi as part of the "Germany MCP factory". Not affiliated with ferien-api.de / the bundesAPI project.
|
|
20
|
+
|
|
21
|
+
## For AI agents
|
|
22
|
+
|
|
23
|
+
Add to your MCP client config (stdio):
|
|
24
|
+
|
|
25
|
+
```json
|
|
26
|
+
{
|
|
27
|
+
"mcpServers": {
|
|
28
|
+
"ferien": {
|
|
29
|
+
"command": "uvx",
|
|
30
|
+
"args": ["ferien-mcp==0.1.0"]
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Or run locally: `python -m ferien_mcp.server` (needs `mcp<2`, `httpx`).
|
|
37
|
+
|
|
38
|
+
## Tools
|
|
39
|
+
|
|
40
|
+
| Tool | Description |
|
|
41
|
+
|------|-------------|
|
|
42
|
+
| `ferien_list_holidays` | School holidays of one year in one federal state (optional month filter) |
|
|
43
|
+
| `ferien_is_holiday` | Is a date (YYYY-MM-DD) inside the school holidays in one state? |
|
|
44
|
+
| `ferien_next_holiday` | The next school holiday on or after a date (returns the ongoing one if inside a holiday; looks into the next year) |
|
|
45
|
+
| `ferien_holidays_between` | School holidays overlapping a date range (max ~2 years) |
|
|
46
|
+
| `ferien_list_states` | All 16 German federal states |
|
|
47
|
+
| `ferien_health` | Reachability check (ok / blocked / error) — one polite request |
|
|
48
|
+
|
|
49
|
+
Example output (`ferien_is_holiday` / 2026-08-10, BY):
|
|
50
|
+
|
|
51
|
+
```json
|
|
52
|
+
{"date": "2026-08-10", "weekday": "Monday", "weekday_de": "Montag",
|
|
53
|
+
"state": "BY", "state_name": "Bayern", "is_holiday": true,
|
|
54
|
+
"holidays": ["sommerferien bayern 2026"],
|
|
55
|
+
"details": [{"name": "sommerferien bayern 2026", "kind": "sommerferien",
|
|
56
|
+
"slug": "sommerferien bayern 2026-2026-BY", "state": "BY",
|
|
57
|
+
"year": 2026, "start": "2026-08-03", "end": "2026-09-15",
|
|
58
|
+
"duration_days": 44, "start_weekday": "Monday",
|
|
59
|
+
"start_weekday_de": "Montag", "end_weekday": "Tuesday",
|
|
60
|
+
"end_weekday_de": "Dienstag"}]}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Notes:
|
|
64
|
+
- State codes are the 2-letter codes: BW, BY, BE, BB, HB, HH, HE, MV, NI, NW, RP, SL, SN, ST, SH, TH. There is **no nationwide list** — school holidays are per-state, so every query needs one state. Use `ferien_list_states` for the full list.
|
|
65
|
+
- Holiday names are German ("sommerferien bayern 2026"); each row also carries a `kind` (winterferien, frühjahrsferien, osterferien, pfingstferien, sommerferien, herbstferien, weihnachtsferien, beweglicher ferientag) and the duration in days.
|
|
66
|
+
- **Coverage varies by state and year**: the API returns an empty list for a state/year it has no data for — e.g. Berlin 2026 was empty on 2026-11-14 while Berlin 2025 has 7 entries. Verify important dates against the official source.
|
|
67
|
+
- Cross-year holidays (e.g. Weihnachtsferien running into January) are included by `ferien_holidays_between` when they overlap the range.
|
|
68
|
+
- Years 2000–2100 are accepted.
|
|
69
|
+
- The API rate-limits aggressively per IP (its docs: "heavily abused, aggressive rate limiting is in place"). If a call reports HTTP 429, wait a few minutes and retry — on 2026-11-14 the limit cleared after ~10 minutes.
|
|
70
|
+
|
|
71
|
+
Built by [Mohammad Abbasi](https://github.com/mohammad-abbasi-sh) — [Almanibama](https://almanibama.ir) · [mabbasi.ir](https://mabbasi.ir)
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# ferien-mcp 🏖️
|
|
2
|
+
|
|
3
|
+
mcp-name: io.github.mohammad-abbasi-sh/ferien-mcp
|
|
4
|
+
|
|
5
|
+
Inoffizieller **Nur-Lesen**-MCP-Server für **deutsche Schulferien** über die [ferien-api.de API](https://ferien-api.de) (bundesAPI-Projekt): Ferien pro Jahr und Bundesland, Ist-es-ein-Ferientag-Prüfung, die nächsten Ferien ab einem Datum und Ferien in einem Zeitraum. Kein Schlüssel, keine Anmeldung nötig.
|
|
6
|
+
|
|
7
|
+
> Gebaut von Mohammad Abbasi im Rahmen der „Germany MCP factory". Keine Verbindung zu ferien-api.de / dem bundesAPI-Projekt.
|
|
8
|
+
|
|
9
|
+
## Für KI-Agenten
|
|
10
|
+
|
|
11
|
+
In die MCP-Client-Konfiguration eintragen (stdio):
|
|
12
|
+
|
|
13
|
+
```json
|
|
14
|
+
{
|
|
15
|
+
"mcpServers": {
|
|
16
|
+
"ferien": {
|
|
17
|
+
"command": "uvx",
|
|
18
|
+
"args": ["ferien-mcp==0.1.0"]
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Oder lokal starten: `python -m ferien_mcp.server` (benötigt `mcp<2`, `httpx`).
|
|
25
|
+
|
|
26
|
+
## Tools
|
|
27
|
+
|
|
28
|
+
| Tool | Beschreibung |
|
|
29
|
+
|------|--------------|
|
|
30
|
+
| `ferien_list_holidays` | Schulferien eines Jahres in einem Bundesland (optional nach Monat gefiltert) |
|
|
31
|
+
| `ferien_is_holiday` | Liegt ein Datum (YYYY-MM-DD) in den Schulferien eines Bundeslandes? |
|
|
32
|
+
| `ferien_next_holiday` | Die nächsten Schulferien ab einem Datum (gibt die laufenden Ferien zurück, wenn das Datum in den Ferien liegt; blickt auch ins nächste Jahr) |
|
|
33
|
+
| `ferien_holidays_between` | Schulferien, die einen Zeitraum überschneiden (max. ca. 2 Jahre) |
|
|
34
|
+
| `ferien_list_states` | Alle 16 Bundesländer |
|
|
35
|
+
| `ferien_health` | Erreichbarkeitsprüfung (ok / blocked / error) — eine höfliche Anfrage |
|
|
36
|
+
|
|
37
|
+
Beispielausgabe (`ferien_is_holiday` / 2026-08-10, BY):
|
|
38
|
+
|
|
39
|
+
```json
|
|
40
|
+
{"date": "2026-08-10", "weekday": "Monday", "weekday_de": "Montag",
|
|
41
|
+
"state": "BY", "state_name": "Bayern", "is_holiday": true,
|
|
42
|
+
"holidays": ["sommerferien bayern 2026"],
|
|
43
|
+
"details": [{"name": "sommerferien bayern 2026", "kind": "sommerferien",
|
|
44
|
+
"slug": "sommerferien bayern 2026-2026-BY", "state": "BY",
|
|
45
|
+
"year": 2026, "start": "2026-08-03", "end": "2026-09-15",
|
|
46
|
+
"duration_days": 44, "start_weekday": "Monday",
|
|
47
|
+
"start_weekday_de": "Montag", "end_weekday": "Tuesday",
|
|
48
|
+
"end_weekday_de": "Dienstag"}]}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Hinweise:
|
|
52
|
+
- Bundesland-Codes sind die 2-Buchstaben-Codes: BW, BY, BE, BB, HB, HH, HE, MV, NI, NW, RP, SL, SN, ST, SH, TH. Es gibt **keine bundesweite Liste** — Schulferien sind Ländersache, jede Abfrage braucht ein Bundesland. `ferien_list_states` liefert die vollständige Liste.
|
|
53
|
+
- Feriennamen sind deutsch („sommerferien bayern 2026"); jede Zeile trägt zusätzlich eine `kind` (winterferien, frühjahrsferien, osterferien, pfingstferien, sommerferien, herbstferien, weihnachtsferien, beweglicher ferientag) und die Dauer in Tagen.
|
|
54
|
+
- **Die Abdeckung variiert je nach Bundesland und Jahr**: Die API liefert eine leere Liste für ein Bundesland/Jahr, zu dem sie keine Daten hat — z. B. war Berlin 2026 am 2026-11-14 leer, während Berlin 2025 sieben Einträge hat. Wichtige Termine bitte an der offiziellen Quelle prüfen.
|
|
55
|
+
- Jahresübergreifende Ferien (z. B. Weihnachtsferien bis in den Januar) werden von `ferien_holidays_between` einbezogen, wenn sie den Zeitraum überschneiden.
|
|
56
|
+
- Jahre 2000–2100 werden akzeptiert.
|
|
57
|
+
- Die API setzt aggressive Ratenbegrenzungen pro IP (laut Doku: „heavily abused, aggressive rate limiting is in place"). Meldet ein Aufruf HTTP 429, ein paar Minuten warten und erneut versuchen — am 2026-11-14 war die Sperre nach ca. 10 Minuten wieder aufgehoben.
|
|
58
|
+
|
|
59
|
+
Built by [Mohammad Abbasi](https://github.com/mohammad-abbasi-sh) — [Almanibama](https://almanibama.ir) · [mabbasi.ir](https://mabbasi.ir)
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# ferien-mcp 🏖️
|
|
2
|
+
|
|
3
|
+
mcp-name: io.github.mohammad-abbasi-sh/ferien-mcp
|
|
4
|
+
|
|
5
|
+
سرور MCP غیررسمی و **فقطخواندنی** برای **تعطیلات مدارس آلمان** (Schulferien) از طریق [API سایت ferien-api.de](https://ferien-api.de) (پروژهٔ bundesAPI): تعطیلات هر سال در هر ایالت فدرال، بررسی اینکه یک تاریخ در تعطیلات مدارس است یا نه، نزدیکترین تعطیلات از یک تاریخ، و تعطیلات در یک بازهٔ زمانی. بدون کلید و بدون ورود.
|
|
6
|
+
|
|
7
|
+
> ساختهٔ محمد عباسی در چارچوب «کارخانهٔ MCP آلمان». وابسته به ferien-api.de / پروژهٔ bundesAPI نیست.
|
|
8
|
+
|
|
9
|
+
## برای ایجنتهای هوش مصنوعی
|
|
10
|
+
|
|
11
|
+
به پیکربندی MCP Client اضافه کنید (stdio):
|
|
12
|
+
|
|
13
|
+
```json
|
|
14
|
+
{
|
|
15
|
+
"mcpServers": {
|
|
16
|
+
"ferien": {
|
|
17
|
+
"command": "uvx",
|
|
18
|
+
"args": ["ferien-mcp==0.1.0"]
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
یا بهصورت محلی اجرا کنید: `python -m ferien_mcp.server` (نیازمند `mcp<2` و `httpx`).
|
|
25
|
+
|
|
26
|
+
## ابزارها
|
|
27
|
+
|
|
28
|
+
| ابزار | توضیح |
|
|
29
|
+
|------|-------|
|
|
30
|
+
| `ferien_list_holidays` | تعطیلات مدارس یک سال در یک ایالت فدرال (با فیلتر اختیاری ماه) |
|
|
31
|
+
| `ferien_is_holiday` | آیا یک تاریخ (YYYY-MM-DD) در تعطیلات مدارس یک ایالت است؟ |
|
|
32
|
+
| `ferien_next_holiday` | نزدیکترین تعطیلات مدارس از یک تاریخ به بعد (اگر تاریخ در تعطیلات باشد، همان تعطیلات جاری را برمیگرداند؛ به سال بعد هم نگاه میکند) |
|
|
33
|
+
| `ferien_holidays_between` | تعطیلات مدارسی که با یک بازهٔ زمانی همپوشانی دارند (حداکثر حدود ۲ سال) |
|
|
34
|
+
| `ferien_list_states` | هر ۱۶ ایالت فدرال |
|
|
35
|
+
| `ferien_health` | بررسی دسترسی به API (ok / blocked / error) — یک درخواست مؤدبانه |
|
|
36
|
+
|
|
37
|
+
نمونهٔ خروجی (`ferien_is_holiday` / ۲۰۲۶-۰۸-۱۰، BY):
|
|
38
|
+
|
|
39
|
+
```json
|
|
40
|
+
{"date": "2026-08-10", "weekday": "Monday", "weekday_de": "Montag",
|
|
41
|
+
"state": "BY", "state_name": "Bayern", "is_holiday": true,
|
|
42
|
+
"holidays": ["sommerferien bayern 2026"],
|
|
43
|
+
"details": [{"name": "sommerferien bayern 2026", "kind": "sommerferien",
|
|
44
|
+
"slug": "sommerferien bayern 2026-2026-BY", "state": "BY",
|
|
45
|
+
"year": 2026, "start": "2026-08-03", "end": "2026-09-15",
|
|
46
|
+
"duration_days": 44, "start_weekday": "Monday",
|
|
47
|
+
"start_weekday_de": "Montag", "end_weekday": "Tuesday",
|
|
48
|
+
"end_weekday_de": "Dienstag"}]}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
نکتهها:
|
|
52
|
+
- کد ایالتها دو حرفی است: BW، BY، BE، BB، HB، HH، HE، MV، NI، NW، RP، SL، SN، ST، SH، TH. **فهرست سراسری وجود ندارد** — تعطیلات مدارس به ایالت بستگی دارد، پس هر درخواست به یک ایالت نیاز دارد. فهرست کامل را از `ferien_list_states` بگیرید.
|
|
53
|
+
- نام تعطیلات آلمانی است («sommerferien bayern 2026»)؛ هر ردیف یک `kind` هم دارد (winterferien، frühjahrsferien، osterferien، pfingstferien، sommerferien، herbstferien، weihnachtsferien، beweglicher ferientag) و مدت آن به روز.
|
|
54
|
+
- **پوشش دادهها بسته به ایالت و سال فرق میکند**: API برای ایالت/سالی که داده ندارد فهرست خالی برمیگرداند — مثلاً برلین ۲۰۲۶ در ۲۰۲۶-۱۱-۱۴ خالی بود، در حالی که برلین ۲۰۲۵ هفت ورودی دارد. تاریخهای مهم را با منبع رسمی بررسی کنید.
|
|
55
|
+
- تعطیلاتِ میانسالی (مثلاً Weihnachtsferien که تا ژانویه ادامه دارد) در `ferien_holidays_between` لحاظ میشوند اگر با بازه همپوشانی داشته باشند.
|
|
56
|
+
- سالهای ۲۰۰۰ تا ۲۱۰۰ پذیرفته میشوند.
|
|
57
|
+
- این API برای هر IP محدودیت نرخ سختگیرانه دارد (به گفتهٔ مستنداتش: «heavily abused, aggressive rate limiting is in place»). اگر پاسخی با HTTP 429 گرفتید، چند دقیقه صبر کنید و دوباره تلاش کنید — در ۲۰۲۶-۱۱-۱۴ محدودیت بعد از حدود ۱۰ دقیقه برطرف شد.
|
|
58
|
+
|
|
59
|
+
Built by [Mohammad Abbasi](https://github.com/mohammad-abbasi-sh) — [Almanibama](https://almanibama.ir) · [mabbasi.ir](https://mabbasi.ir)
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# ferien-mcp 🏖️
|
|
2
|
+
|
|
3
|
+
mcp-name: io.github.mohammad-abbasi-sh/ferien-mcp
|
|
4
|
+
|
|
5
|
+
Unofficial **read-only** MCP server for **German school holidays** (Schulferien) via the [ferien-api.de API](https://ferien-api.de) (bundesAPI project): holidays per year and federal state, is-it-a-holiday checks, the next holiday from a date, and holidays in a date range. No key, no login.
|
|
6
|
+
|
|
7
|
+
> Built by Mohammad Abbasi as part of the "Germany MCP factory". Not affiliated with ferien-api.de / the bundesAPI project.
|
|
8
|
+
|
|
9
|
+
## For AI agents
|
|
10
|
+
|
|
11
|
+
Add to your MCP client config (stdio):
|
|
12
|
+
|
|
13
|
+
```json
|
|
14
|
+
{
|
|
15
|
+
"mcpServers": {
|
|
16
|
+
"ferien": {
|
|
17
|
+
"command": "uvx",
|
|
18
|
+
"args": ["ferien-mcp==0.1.0"]
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Or run locally: `python -m ferien_mcp.server` (needs `mcp<2`, `httpx`).
|
|
25
|
+
|
|
26
|
+
## Tools
|
|
27
|
+
|
|
28
|
+
| Tool | Description |
|
|
29
|
+
|------|-------------|
|
|
30
|
+
| `ferien_list_holidays` | School holidays of one year in one federal state (optional month filter) |
|
|
31
|
+
| `ferien_is_holiday` | Is a date (YYYY-MM-DD) inside the school holidays in one state? |
|
|
32
|
+
| `ferien_next_holiday` | The next school holiday on or after a date (returns the ongoing one if inside a holiday; looks into the next year) |
|
|
33
|
+
| `ferien_holidays_between` | School holidays overlapping a date range (max ~2 years) |
|
|
34
|
+
| `ferien_list_states` | All 16 German federal states |
|
|
35
|
+
| `ferien_health` | Reachability check (ok / blocked / error) — one polite request |
|
|
36
|
+
|
|
37
|
+
Example output (`ferien_is_holiday` / 2026-08-10, BY):
|
|
38
|
+
|
|
39
|
+
```json
|
|
40
|
+
{"date": "2026-08-10", "weekday": "Monday", "weekday_de": "Montag",
|
|
41
|
+
"state": "BY", "state_name": "Bayern", "is_holiday": true,
|
|
42
|
+
"holidays": ["sommerferien bayern 2026"],
|
|
43
|
+
"details": [{"name": "sommerferien bayern 2026", "kind": "sommerferien",
|
|
44
|
+
"slug": "sommerferien bayern 2026-2026-BY", "state": "BY",
|
|
45
|
+
"year": 2026, "start": "2026-08-03", "end": "2026-09-15",
|
|
46
|
+
"duration_days": 44, "start_weekday": "Monday",
|
|
47
|
+
"start_weekday_de": "Montag", "end_weekday": "Tuesday",
|
|
48
|
+
"end_weekday_de": "Dienstag"}]}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Notes:
|
|
52
|
+
- State codes are the 2-letter codes: BW, BY, BE, BB, HB, HH, HE, MV, NI, NW, RP, SL, SN, ST, SH, TH. There is **no nationwide list** — school holidays are per-state, so every query needs one state. Use `ferien_list_states` for the full list.
|
|
53
|
+
- Holiday names are German ("sommerferien bayern 2026"); each row also carries a `kind` (winterferien, frühjahrsferien, osterferien, pfingstferien, sommerferien, herbstferien, weihnachtsferien, beweglicher ferientag) and the duration in days.
|
|
54
|
+
- **Coverage varies by state and year**: the API returns an empty list for a state/year it has no data for — e.g. Berlin 2026 was empty on 2026-11-14 while Berlin 2025 has 7 entries. Verify important dates against the official source.
|
|
55
|
+
- Cross-year holidays (e.g. Weihnachtsferien running into January) are included by `ferien_holidays_between` when they overlap the range.
|
|
56
|
+
- Years 2000–2100 are accepted.
|
|
57
|
+
- The API rate-limits aggressively per IP (its docs: "heavily abused, aggressive rate limiting is in place"). If a call reports HTTP 429, wait a few minutes and retry — on 2026-11-14 the limit cleared after ~10 minutes.
|
|
58
|
+
|
|
59
|
+
Built by [Mohammad Abbasi](https://github.com/mohammad-abbasi-sh) — [Almanibama](https://almanibama.ir) · [mabbasi.ir](https://mabbasi.ir)
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "ferien-mcp"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Unofficial read-only MCP server for German school holidays (Schulferien) — holidays by year & state, is-it-a-holiday, next holiday & date ranges"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = { text = "MIT" }
|
|
11
|
+
requires-python = ">=3.10"
|
|
12
|
+
dependencies = [
|
|
13
|
+
"mcp<2",
|
|
14
|
+
"httpx>=0.27",
|
|
15
|
+
]
|
|
16
|
+
authors = [{ name = "Mohammad Abbasi" }]
|
|
17
|
+
|
|
18
|
+
[project.scripts]
|
|
19
|
+
ferien-mcp = "ferien_mcp.server:main"
|
|
20
|
+
|
|
21
|
+
[tool.hatch.build.targets.wheel]
|
|
22
|
+
packages = ["src/ferien_mcp"]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"$schema":"https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json","description":"Unofficial read-only MCP server for German school holidays — by year & state, next holiday.","name":"io.github.mohammad-abbasi-sh/ferien-mcp","packages":[{"identifier":"ferien-mcp","registryBaseUrl":"https://pypi.org","registryType":"pypi","transport":{"type":"stdio"},"version":"0.1.0"}],"repository":{"source":"github","url":"https://github.com/mohammad-abbasi-sh/ferien-mcp"},"title":"Ferien — German school holidays by year & federal state","version":"0.1.0","websiteUrl":"https://github.com/mohammad-abbasi-sh/ferien-mcp"}
|