[build-system] requires = ["hatchling"] build-backend = "hatchling.build" [project] name = "seco-labor-mcp" version = "0.4.0" description = "MCP server for Swiss labor market data (SECO/AMSTAT) – Arbeitsmarkt & Stellensuche" readme = "README.md" license = { text = "MIT" } requires-python = ">=3.11" authors = [{ name = "malkreide" }] keywords = [ "mcp", "model-context-protocol", "swiss-open-data", "seco", "labor-market", "arbeitsmarkt", "unemployment", "arbeitslosigkeit", ] classifiers = [ "Development Status :: 4 - Beta", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Topic :: Scientific/Engineering :: Artificial Intelligence", ] dependencies = [ # Obergrenzen als Schutz vor Major-Bumps, die ohne Codeaenderung einschlagen. # # fastmcp 4.x ist die Untergrenze, nicht bloss die Obergrenze: erst diese # Linie zieht `mcp` 2.x herein, und erst `mcp` 2.x kennt die Protokoll-Aera # 2026-07-28. Unter fastmcp 3.x (`mcp` 1.30.0) ist 2025-11-25 die hoechste # Revision, die es ueberhaupt gibt -- der Server konnte die neue Spec dort # nicht sprechen, auch nicht halb. Der frueher hier notierte Sprung ist # damit vollzogen; ein `>=1.0.0` waere jetzt ein Rueckfall in die alte Aera, # ohne dass etwas rot wird. # # Verifiziert mit 4.0.5 / `mcp` 2.2.0: gemessen, nicht geschlossen -- # `tests/test_protokoll_aeren.py` verhandelt beide Aeren gegen genau dieses # Server-Objekt. Die Obergrenze `<5` bleibt der Schutz vor dem naechsten # Major, das ungefragt einzoege. "fastmcp>=4.0.0,<5", # httpx ist pre-1.0; auf PyPI stehen bereits 1.0.dev-Releases. Das Major # bricht erfahrungsgemaess Transport- und Mock-Verhalten (respx). "httpx>=0.27.0,<1", # Pydantic v2 ist die Portfolio-Konvention. v3 waere ein Modellbruch. "pydantic>=2.0.0,<3", # pypdf fuer die UVG-Tabellen. Die Quelle publiziert ausschliesslich PDF # (siehe PROBE_REPORT_UVG.md), es gibt kein CSV/JSON. Der Parser haengt am # layout-Extraktionsmodus, dessen Whitespace-Verhalten die Spaltentrennung # traegt -- ein Major-Bump darf hier nicht ungefragt einziehen. "pypdf>=5.0.0,<7", # openpyxl fuer die BFS-Jahrestabelle. Die Reihe der registrierten # Arbeitslosen und Stellensuchenden gibt es nur als XLSX -- weder CSV noch # SDMX, geprueft am 2026-08-14. Der Parser haengt an der Zellenlesung von # `read_only`-Arbeitsmappen; ein Major-Bump darf hier nicht ungefragt # einziehen. "openpyxl>=3.1.0,<4", ] [project.optional-dependencies] dev = [ "pytest>=8.0.0", "pytest-asyncio>=0.24.0", "pytest-cov>=5.0.0", "respx>=0.21.0", # Exakter Pin, kein Range: ruff ist pre-1.0, seine Minors sind die Stelle, # an der Regelverhalten und neue Checks innerhalb der gewaehlten Familien # landen. Ein Range wird ohne Codeaenderung rot. # # Vorher stand hier `>=0.5.0,<0.17`. Der Cap allein genuegt nicht: die CI # faehrt `ruff==0.16.5`, ein `pip install -e ".[dev]"` erfuellt die Spanne # aber mit einer beliebigen Version darin. Lokal gruen und in der CI rot # war damit moeglich, ohne dass jemand etwas verursacht hat. # # Diese Zeile ist die einzige Quelle: Anheben genuegt hier, `ci.yml` # installiert keine zweite Version mehr. Der Schritt `pip install # ruff==...`, auf den dieser Absatz frueher verwies, ist entfernt — er # lief nach dem dev-Install und gewann gegen diese Zeile, sodass ein Bump # hier die CI nicht erreichte. `tests/test_werkzeug_versionen.py` faellt, # wenn wieder ein Workflow eine Version setzt. # # Vor dem Lauf `ruff --version` pruefen: ein aelteres ruff frueher im # `PATH` schlaegt den Pin, ohne dass der Install etwas meldet. # `scripts/check_ruff_pin.py` prueft genau das. "ruff==0.16.5", ] [project.scripts] seco-labor-mcp = "seco_labor_mcp.server:main" [project.urls] Homepage = "https://github.com/malkreide/seco-labor-mcp" Repository = "https://github.com/malkreide/seco-labor-mcp" Issues = "https://github.com/malkreide/seco-labor-mcp/issues" [tool.hatch.build.targets.wheel] packages = ["src/seco_labor_mcp"] [tool.pytest.ini_options] asyncio_mode = "auto" testpaths = ["tests"] addopts = "-v --tb=short" markers = [ "live: live API tests (skipped in CI by default)", ] [tool.ruff] line-length = 100 target-version = "py311" [tool.ruff.lint] select = ["E", "F", "W", "I", "UP"] ignore = ["E501"]