π¨π Part of the Swiss Public Data MCP Portfolio
π zh-education-mcp
MCP server for education statistics of the Canton and City of Zurich (BISTA)
Overview
zh-education-mcp connects AI assistants to the Bildungsstatistik Kanton ZΓΌrich (BISTA) β the official education statistics of the Canton of Zurich. It provides structured access to pupil numbers, school district trends, secondary school profiles, nationality breakdowns, and gymnasium graduation rates.
| Source | Data | API |
|---|---|---|
| BISTA Kanton ZΓΌrich | Learner statistics (Volksschule, Mittelschulen, MaturitΓ€t) | REST/CSV |
All data is fetched from the BISTA public API (bista.zh.ch/basicapi/ogd/) β no API key required. Data is updated annually on 15 September (reference date).
Anchor demo query: "How has the number of pupils in school district Letzi developed over the last 5 years?"
Demo
<p align="center"> <img src="assets/demo.png" alt="zh-education-mcp demo: Claude queries BISTA data" width="720"> </p>Features
- π 8 tools for education data across all school levels
- π School district trends β pupil numbers for all Schulkreise (Letzi, Glattal, Schwamendingen, Oerlikon, Uto, Waidberg, ZΓΌrichberg) from 2000 to present
- π« Secondary school profiles β breakdown by requirement type (Sek A/B/C, Mittelschule, special classes)
- π Nationality structure β top nationalities of pupils per school community
- π Gymnasium graduation rates β MaturitΓ€tsquote by municipality, district, and canton
- π Canton-wide overview β all learners by school level, type, gender, and nationality
- π Residence-based trends β pupil counts by place of residence (Bezirk / Gemeinde)
- ποΈ Mittelschulen β Gymnasium, FMS, HMS statistics
- π No API key required β all data under CC BY 4.0
- βοΈ Dual transport β stdio (Claude Desktop) + Streamable HTTP (cloud)
Prerequisites
- Python 3.11+
- uv (recommended) or pip
Installation
# Clone the repository
git clone https://github.com/malkreide/zh-education-mcp.git
cd zh-education-mcp
# Install
pip install -e .
# or with uv:
uv pip install -e .
Or with uvx (no permanent installation):
uvx zh-education-mcp
Quickstart
# stdio (for Claude Desktop)
python -m zh_education_mcp.server
# Streamable HTTP (port 8000)
python -m zh_education_mcp.server --http --port 8000
Try it immediately in Claude Desktop:
"Wie hat sich die Lernendenzahl im Schulkreis Letzi entwickelt?" "Zeige die MaturitΓ€tsquote der Stadt ZΓΌrich" "Welche NationalitΓ€ten sind in Adliswil am hΓ€ufigsten?"
β More use cases by audience β
Configuration
Claude Desktop
Edit ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows):
{
"mcpServers": {
"zh-education": {
"command": "python",
"args": ["-m", "zh_education_mcp.server"]
}
}
}
Or with uvx:
{
"mcpServers": {
"zh-education": {
"command": "uvx",
"args": ["zh-education-mcp"]
}
}
}
Config file locations:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json
Cloud Deployment (Streamable HTTP for browser access)
For use via claude.ai in the browser (e.g. on managed workstations without local software):
Render.com (recommended):
- Push/fork the repository to GitHub
- On render.com: New Web Service β runtime Docker (uses the provided
Dockerfile) - Set env vars:
MCP_TRANSPORT=streamable-http,MCP_HOST=0.0.0.0,MCP_PORT=8000, andMCP_CORS_ORIGINS=https://claude.ai - In claude.ai under Settings β MCP Servers, add:
https://your-app.onrender.com/mcp
π‘ "stdio for the developer laptop, Streamable HTTP for the browser."
Health probe: GET /health. Full deployment guide (container, load balancing, CORS,
resource limits): docs/deployment.md.
Available Tools
| Tool | Description |
|---|---|
zh_edu_list_schulgemeinden | List all school communities / Schulkreise in Canton Zurich |
zh_edu_schulkreis_trend | Pupil trend by Schulkreis (2000βpresent) |
zh_edu_overview | Canton-wide learner overview by school level |
zh_edu_sek1_profil | Secondary I profile (Sek A/B/C breakdown) |
zh_edu_staatsangehoerigkeiten | Nationality structure of pupils per school community |
zh_edu_maturitaetsquote | Gymnasium graduation rates by municipality / district |
zh_edu_wohnort_trend | Residence-based learner trend (Bezirk / Gemeinde) |
zh_edu_mittelschulen | Secondary school statistics (Gymnasium, FMS, HMS) |
Example Use Cases
| Query | Tool |
|---|---|
| "List all Schulkreise in Zurich" | zh_edu_list_schulgemeinden |
| "Pupil trend in Letzi over 5 years" | zh_edu_schulkreis_trend |
| "How many Sek A vs Sek B in Winterthur?" | zh_edu_sek1_profil |
| "Top nationalities in ZΓΌrich-Letzi" | zh_edu_staatsangehoerigkeiten |
| "MaturitΓ€tsquote of Stadt ZΓΌrich" | zh_edu_maturitaetsquote |
Architecture
βββββββββββββββββββ ββββββββββββββββββββββββββββββββ ββββββββββββββββββββββββββββ
β Claude / AI ββββββΆβ zh-education-mcp ββββββΆβ BISTA Kanton ZΓΌrich β
β (MCP Host) βββββββ (MCP Server) βββββββ REST/CSV (Public API) β
βββββββββββββββββββ β β ββββββββββββββββββββββββββββ
β 8 Tools β
β Stdio | Streamable HTTP β
β 24h Cache β
β No authentication required β
ββββββββββββββββββββββββββββββββ
Data Source Characteristics
| Source | Protocol | Coverage | Auth | Update |
|---|---|---|---|---|
| BISTA Kanton ZH | REST/CSV | Learner statistics 2000βpresent | None | Annual (15 Sep) |
Project Structure
zh-education-mcp/
βββ src/zh_education_mcp/
β βββ __init__.py # Package
β βββ config.py # ENV settings (MCP_*)
β βββ constants.py # API base, endpoints, timeouts
β βββ logging_setup.py # structured stderr logging
β βββ provenance.py # response envelope, license attribution
β βββ http_client.py # egress guard, connection pool, lifespan
β βββ data.py # cache, CSV fetch, filters, error handling
β βββ models.py # Pydantic input models
β βββ tools.py # FastMCP instance, 8 tools, 2 resources
β βββ server.py # thin composition layer + entrypoint
βββ tests/
β βββ test_server.py # Unit tests (mocked HTTP with respx)
βββ docs/ # deployment, security, egress, roadmap, β¦
βββ Dockerfile # multi-stage, non-root, healthcheck
βββ docker-compose.yml # resource limits, read-only rootfs
βββ .github/workflows/ci.yml # GitHub Actions (Python 3.11/3.12/3.13)
βββ .github/dependabot.yml # monthly dependency updates
βββ pyproject.toml
βββ CHANGELOG.md
βββ CONTRIBUTING.md # + CONTRIBUTING.de.md
βββ SECURITY.md # + SECURITY.de.md
βββ LICENSE
βββ README.md # This file (English)
βββ README.de.md # German version
Known Limitations
- Annual updates only: BISTA data is updated once per year (reference date: 15 September). The 24h in-memory cache matches this cycle.
- CSV-based API: The BISTA API returns CSV data; large datasets may take a moment to parse.
- School community names: Names must match exactly (use
zh_edu_list_schulgemeindento find valid names).
Safety & Limits
| Topic | Details |
|---|---|
| No personal data | BISTA statistics are aggregated β no individual pupil data is exposed or accessible. All figures are anonymized at the school community level. |
| Read-only | All tools are read-only (readOnlyHint: true). The server cannot modify, delete, or write any data. |
| No authentication | The BISTA API is fully public. No API keys, tokens, or credentials are stored or transmitted. |
| Rate limits | The BISTA API has no documented rate limit, but the server uses a 24h in-memory cache to minimize requests. Please use responsibly. |
| Data license | All data is published under CC BY 4.0 by the Canton of Zurich. Attribution: Bildungsstatistik Kanton ZΓΌrich (BISTA). |
| Terms of Service | Usage is subject to the BISTA terms of use. The MCP server is an independent open-source project and is not affiliated with the Canton of Zurich. |
| AI output disclaimer | Statistics are passed through as-is from the BISTA API. AI-generated interpretations or summaries should be verified against the official BISTA portal. |
Testing
# Unit tests (no API calls)
PYTHONPATH=src pytest tests/ -m "not live"
# Integration tests (live API calls)
pytest tests/ -m "live"
MCP Protocol Version
This server targets the MCP specification as implemented by the pinned mcp[cli]
SDK (see pyproject.toml). Protocol/spec-version bumps are recorded in
CHANGELOG.md. Dependencies (incl. the MCP SDK) receive monthly
update PRs via Dependabot (.github/dependabot.yml).
Project phase: Phase 1 β read-only (all tools readOnlyHint: true). See
docs/roadmap.md.
Changelog
See CHANGELOG.md
Contributing
See CONTRIBUTING.md Β· π©πͺ Beitragen
Security
See SECURITY.md Β· π©πͺ Sicherheit
License
MIT License β see LICENSE
Author
Hayal Oezkan Β· malkreide
Credits & Related Projects
- BISTA: pub.bista.zh.ch β Bildungsstatistik Kanton ZΓΌrich (CC BY 4.0)
- Protocol: Model Context Protocol β Anthropic / Linux Foundation
- Related: zurich-opendata-mcp β Zurich city open data (parking, weather, parliament)
- Related: swiss-cultural-heritage-mcp β Swiss cultural heritage data
- Related: fedlex-mcp β Swiss federal law
- Portfolio: Swiss Public Data MCP Portfolio
Installation
Run via uv's uvx β no clone or manual install needed. Add to your MCP client config (mcpServers for Claude Desktop, Cursor and Windsurf; use a top-level servers key for VS Code in .vscode/mcp.json):
{
"mcpServers": {
"zh-education-mcp": {
"command": "uvx",
"args": [
"zh-education-mcp"
]
}
}
}